eva2/src/eva2/optimization/strategies/ParticleSwarmOptimization.java
Fabian Becker 4326044a1b Organize imports.
Fix missing data header in F8Problem.
Fix several tip texts.
2014-11-13 14:50:13 +01:00

1961 lines
75 KiB
Java

package eva2.optimization.strategies;
import eva2.gui.BeanInspector;
import eva2.gui.editor.GenericObjectEditor;
import eva2.gui.plot.Plot;
import eva2.gui.plot.TopoPlot;
import eva2.optimization.enums.PSOTopology;
import eva2.optimization.individuals.AbstractEAIndividual;
import eva2.optimization.individuals.EAIndividualComparator;
import eva2.optimization.individuals.InterfaceDataTypeDouble;
import eva2.optimization.operator.distancemetric.PhenotypeMetric;
import eva2.optimization.operator.paramcontrol.ParamAdaption;
import eva2.optimization.operator.paramcontrol.ParameterControlManager;
import eva2.optimization.population.InterfaceSolutionSet;
import eva2.optimization.population.Population;
import eva2.optimization.population.PopulationInterface;
import eva2.optimization.population.SolutionSet;
import eva2.problems.Interface2DBorderProblem;
import eva2.problems.InterfaceAdditionalPopulationInformer;
import eva2.problems.InterfaceProblemDouble;
import eva2.tools.chart2d.DPoint;
import eva2.tools.chart2d.DPointSet;
import eva2.tools.math.Jama.Matrix;
import eva2.tools.math.Mathematics;
import eva2.tools.math.RNG;
import eva2.util.annotation.Description;
import eva2.util.annotation.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Vector;
/**
* This implements particle swarm optimization by Kennedy and Eberhardt. Works
* fine but is limited to real-valued genotypes and the original version ignored
* range constraints on the decision variables. I've implemented 'brakes' before
* an individual is updated it is checked whether the new individual would
* violate range constraints, if so the velocity vector is reduced.
* <p>
* Possible topologies are: "Linear", "Grid", "Star", "Multi-Swarm", "Tree",
* "HPSO", "Random" in that order starting by 0.
*/
@Description("Particle Swarm Optimization by Kennedy and Eberhart.")
public class ParticleSwarmOptimization extends AbstractOptimizer implements java.io.Serializable, InterfaceAdditionalPopulationInformer {
public enum PSOType { Inertness, Constriction }
Object[] sortedPop = null;
protected AbstractEAIndividual bestIndividual = null;
protected boolean checkRange = true;
protected boolean checkSpeedLimit = false;
protected boolean useAlternative = false;
protected PSOTopology topology = PSOTopology.grid;
/**
* Defines which version of PSO is applied, classical inertness or
* constriction (using chi)
*/
protected PSOType algType;
protected int topologyRange = 2;
protected double initialVelocity = 0.2;
protected double speedLimit = 0.1;
protected double phi1 = 2.05;
protected double phi2 = 2.05;
// for multi-swarm topology: radius of the swarm relative to the range
protected double swarmRadius = 0.2;
// for multi-swarm: maximum sub swarm size. zero means unlimited
protected int maxSubSwarmSize = 0;
protected int minSubSwarmSize = 2;
protected int treeStruct = 1;
protected boolean wrapTopology = true;
protected int treeLevels, treeOrphans, treeLastFullLevelNodeCnt;
protected int dmsRegroupInterval = 10;
private transient Vector<int[]> dmsLinks = null;
protected ParameterControlManager paramControl = new ParameterControlManager();
/**
* InertnessOrChi may contain the inertness or chi parameter depending on
* algoType
*/
protected double inertnessOrChi = 0.73;
protected double reduceSpeed = 0.8;
private double reduceSpeedOnConstViolation = 0.5;
public static final int defaultType = 0;
public static final int resetType = 99;
transient final static String partTypeKey = "ParticleType";
public transient final static String partBestPosKey = "BestPosition";
transient final static String partBestFitKey = "BestFitness";
public transient final static String partVelKey = "Velocity";
transient final static String multiSwTypeKey = "MultiSwarmType";
transient final static String multiSwSizeKey = "MultiSwarmSize";
transient final static String indexKey = "particleIndex";
transient final static String sortedIndexKey = "sortedParticleIndex";
transient final static String dmsGroupIndexKey = "dmsGroupIndex";
protected String indentifier = "";
transient private TopoPlot topoPlot = null;
/// sleep time so that visual plot can be followed easier
protected int sleepTime = 0;
// for tracing the average velocity
transient private double[] tracedVelocity = null;
// parameter for exponential moving average
private int emaPeriods = 0;
// for debugging only
transient protected boolean show = false;
transient protected Plot plot;
private boolean externalInitialPop = false;
private static String lastSuccessKey = "successfulUpdate";
// private double lsCandidateRatio=0.25;
public ParticleSwarmOptimization() {
topology = PSOTopology.grid;
algType = PSOType.Constriction;
setConstriction(getPhi1(), getPhi2());
hideHideable();
}
public ParticleSwarmOptimization(ParticleSwarmOptimization a) {
topology = a.topology;
this.algType = a.algType;
this.population = (Population) a.population.clone();
this.optimizationProblem = a.optimizationProblem;
this.indentifier = a.indentifier;
this.initialVelocity = a.initialVelocity;
this.speedLimit = a.speedLimit;
this.phi1 = a.phi1;
this.phi2 = a.phi2;
this.inertnessOrChi = a.inertnessOrChi;
this.topologyRange = a.topologyRange;
this.paramControl = (ParameterControlManager) a.paramControl.clone();
//this.setCheckSpeedLimit(a.isCheckSpeedLimit());
}
/**
* Constructor for most common parameters with constriction based approach.
*
* @param popSize swarm size
* @param p1 the value for phi1
* @param p2 the value for phi1
* @param topo type of the neighbourhood topology
* @param topoRange range of the neighbourhood topology
*/
public ParticleSwarmOptimization(int popSize, double p1, double p2, PSOTopology topo, int topoRange) {
this();
algType = PSOType.Constriction; // set to constriction
population = new Population(popSize);
setPhiValues(p1, p2);
topologyRange = topoRange;
topology = topo;
}
@Override
public Object clone() {
return new ParticleSwarmOptimization(this);
}
/**
* Take care that all properties which may be hidden (and currently are)
* send a "hide" message to the Java Bean properties. This is called by
* PropertySheetPanel in use with the GenericObjectEditor.
*/
public void hideHideable() {
setCheckSpeedLimit(checkSpeedLimit);
setTopology(getTopology());
}
@Override
public void initialize() {
if (plot != null) {
// plot.dispose();
plot = null;
}
if (topoPlot != null) {
// topoPlot.dispose();
topoPlot = null;
}
tracedVelocity = null;
if (!externalInitialPop) {
this.optimizationProblem.initializePopulation(this.population);
}
// evaluation needs to be done here now, as its omitted if reset is false
initDefaults(this.population);
this.evaluatePopulation(this.population);
if (bestIndividual == null) {
bestIndividual = population.getBestEAIndividual();
}
initializeByPopulation(null, false);
externalInitialPop = false;
}
/**
* Set the initial random velocity vector.
*
* @param indy the individual to work on
* @param initialV initial velocity relative to the range
*/
public static void initIndividualDefaults(AbstractEAIndividual indy, double initialV) {
double[] writeData;
// initialize velocity
writeData = Mathematics.randomVector(((InterfaceDataTypeDouble) indy).getDoubleData().length, 1);
//sum = Math.sqrt(sum);
double relSpeed = Mathematics.getRelativeLength(writeData, ((InterfaceDataTypeDouble) indy).getDoubleRange());
for (int j = 0; j < writeData.length; j++) {
writeData[j] = (writeData[j] / relSpeed) * initialV;
}
indy.putData(partTypeKey, defaultType);
indy.putData(partVelKey, writeData);
}
/**
* Set current position and fitness as best personal position and fitness.
*
* @param indy the individual to work on
*/
protected static void initIndividualMemory(AbstractEAIndividual indy) {
// initialize best fitness
double[] tmpD = indy.getFitness();
double[] writeData = new double[tmpD.length];
System.arraycopy(tmpD, 0, writeData, 0, tmpD.length);
indy.putData(partBestFitKey, writeData);
// initialize best position
tmpD = ((InterfaceDataTypeDouble) indy).getDoubleData();
writeData = new double[tmpD.length];
System.arraycopy(tmpD, 0, writeData, 0, tmpD.length);
indy.putData(partBestPosKey, writeData);
}
/**
* Update the exponential moving average of the population velocity with the
* current population.
*
* @param population
*/
protected void traceEMA(Population population) {
if (population.get(0) instanceof InterfaceDataTypeDouble) {
double[] curAvVelAndSpeed;
if (population.getGeneration() == 0) {
return;
}
curAvVelAndSpeed = getPopulationVelSpeed(population, 3, partVelKey, partTypeKey, defaultType);
double[][] range = ((InterfaceDataTypeDouble) population.get(0)).getDoubleRange();
if (tracedVelocity == null) {
tracedVelocity = new double[((InterfaceDataTypeDouble) population.get(0)).getDoubleData().length];
System.arraycopy(curAvVelAndSpeed, 0, tracedVelocity, 0, tracedVelocity.length);
} else {
if (population.getGeneration() < emaPeriods) {// if less than emaPeriods have passed, use larger alpha
addMovingAverage(tracedVelocity, curAvVelAndSpeed, 2. / (population.getGeneration() + 1));
} else {
addMovingAverage(tracedVelocity, curAvVelAndSpeed, 2. / (emaPeriods + 1));
}
}
if (show) {
System.out.println(population.getGeneration() + " - abs avg " + curAvVelAndSpeed[curAvVelAndSpeed.length - 1] + ", vect " + Mathematics.getRelativeLength(curAvVelAndSpeed, range) + ", rel " + (curAvVelAndSpeed[curAvVelAndSpeed.length - 1] / Mathematics.getRelativeLength(curAvVelAndSpeed, range)));
}
}
}
private void addMovingAverage(double[] speedSoFar, double[] curAvSpeed, double alpha) {
for (int i = 0; i < speedSoFar.length; i++) {
speedSoFar[i] = (1. - alpha) * speedSoFar[i] + alpha * curAvSpeed[i];
}
}
public double[] getEMASpeed() {
return tracedVelocity;
}
public double getRelativeEMASpeed(double[][] range) {
return Mathematics.getRelativeLength(getEMASpeed(), range);
}
/**
* May calculate both cumulative vectorial swarm velocity or the average
* absolute particle speed in the population. The mode switch may be 1 then
* the vectorial swarm velocity is returned; if it is 2, the average speed
* relative to the range is returned (double array of length one); if it is
* 3 both is returned in one array where the average absolute speed is in
* the last entry. The velocity vector key is to be passed over. Optionally,
* an additional identifier is tested - they may be null.
*
* @param pop the swarm population
* @param calcModeSwitch mode switch
* @param velocityKey String to access velocity vector within
* AbstractEAIndividual
* @param typeString optional type string to access individual type
* @param requiredType optional required type identifier of the individual.
* @return double array containing the vectorial sum of particle velocities
* or an array containing the average absolute speed
* @see AbstractEAIndividual#getData(String)
*/
public static double[] getPopulationVelSpeed(Population pop, int calcModeSwitch, final String velocityKey, final String typeString, final Object requiredType) {
AbstractEAIndividual indy = pop.get(0);
if (!(indy instanceof InterfaceDataTypeDouble)) {
System.err.println("error, PSO needs individuals with double data!");
}
double[] ret;
double[][] range = ((InterfaceDataTypeDouble) indy).getDoubleRange();
int retSize = 0;
///// warning, this method uses dark magic
boolean calcVectVelocity = ((calcModeSwitch & 1) > 0);
boolean calcAbsSpeedAverage = ((calcModeSwitch & 2) > 0);
if ((calcModeSwitch & 3) == 0) {
System.err.println("Error, switch must be 1, 2 or 3 (getPopulationVelSpeed)");
}
double[] velocity = (double[]) indy.getData(velocityKey);
if (velocity != null) {
if (calcVectVelocity) {
retSize += velocity.length; // entries for cumulative velocity
}
if (calcAbsSpeedAverage) {
retSize++; // one entry for average absolute speed
} // return length of the array depends on what should be calculated
ret = new double[retSize];
double[] cumulVeloc = new double[velocity.length];
double avSpeed = 0.;
for (int i = 0; i < cumulVeloc.length; i++) {
cumulVeloc[i] = 0.;
}
int indCnt = 0;
for (int i = 0; i < pop.size(); i++) {
indy = pop.get(i);
if (indy.hasData(velocityKey)) {
velocity = (double[]) (indy.getData(velocityKey));
if (velocity != null) {
if ((typeString == null) || (indy.getData(typeString).equals(requiredType))) {
//if (particleHasSpeed(indy)) {
indCnt++;
if (calcVectVelocity) {
for (int j = 0; j < cumulVeloc.length; j++) {
cumulVeloc[j] += velocity[j];
}
}
if (calcAbsSpeedAverage) {
avSpeed += Mathematics.getRelativeLength(velocity, range);
}
}
} else {
System.err.println("Error: Indy without velocity!! (getPopulationVelSpeed)");
}
}
}
if (calcVectVelocity) {
for (int j = 0; j < cumulVeloc.length; j++) {
ret[j] = cumulVeloc[j] / ((double) indCnt);
}
}
if (calcAbsSpeedAverage) {
avSpeed /= ((double) indCnt);
ret[ret.length - 1] = avSpeed;
}
return ret;
} else {
System.err.println("warning, no speed in particle! (getPopulationVelocity)");
return null;
}
//for (int j=0; j<avSpeed.length; j++) avSpeed[j] /= velocity[j];
}
/**
* Check particle type and return true if the particle is 'standard', i.e.
* it has a speed property.
*
* @param indy the individual to check
* @return true if the particle has a speed property
*/
protected boolean particleHasSpeed(AbstractEAIndividual indy) {
return isParticleType(indy, defaultType);
}
protected boolean isParticleTypeByIndex(int index, int type) {
return isParticleType(population.get(index), type);
}
protected boolean isParticleType(AbstractEAIndividual indy, int type) {
return (((Integer) indy.getData(partTypeKey)) == type);
}
/**
* Calculates the vectorial sum of the velocities of all particles in the
* given population.
*
* @param pop
* @return vectorial sum of the particle velocities
*/
public double[] getPopulationVelocity(Population pop) {
return getPopulationVelSpeed(pop, 1, partVelKey, partTypeKey, defaultType);
}
/**
* Calculates the average of the (range-) normed velocities of all particles
* in the given population.
*
* @param pop
* @return average of the (range-) normed velocities
*/
public double getPopulationAvgNormedVelocity(Population pop) {
return getPopulationVelSpeed(pop, 2, partVelKey, partTypeKey, defaultType)[0];
}
/**
* This method will initialize the optimizer with a given population or, if pop is
* null, initialize the current population as if it was new.
*
* @param pop The initial population
* @param reset If true the population is reset.
*/
@Override
public void initializeByPopulation(Population pop, boolean reset) {
if (pop != null) {
this.population = (Population) pop.clone();
externalInitialPop = true;
}
if (reset) {
this.population.initialize();
}
AbstractEAIndividual indy;
if (!defaultsDone(population.getEAIndividual(0))) {
initDefaults(population);
}
if (reset) {
this.evaluatePopulation(this.population);
}
for (int i = 0; i < this.population.size(); i++) {
indy = this.population.get(i);
if (indy instanceof InterfaceDataTypeDouble) {
initIndividualMemory(indy);
}
}
this.bestIndividual = (AbstractEAIndividual) this.population.getBestEAIndividual().clone();
if (reset) {
this.firePropertyChangedEvent(Population.NEXT_GENERATION_PERFORMED);
}
treeLevels = 0;
// the HPSO tree will contain layers 0...HPSOLevels, the last one is "incomplete" with only HPSOOrphans number of nodes
if (getTopology() == PSOTopology.hpso || getTopology() == PSOTopology.tree) {
if (topologyRange < 2) {
System.err.println("Error, tree/hpso requires topology range of at least 2!");
} else {
while (getMaxNodes(topologyRange, treeLevels) < population.size()) {
treeLevels++;
}
treeOrphans = population.size() - getMaxNodes(topologyRange, treeLevels - 1);
treeLastFullLevelNodeCnt = (int) Math.pow(topologyRange, treeLevels - 1);
}
}
if (getTopology() == PSOTopology.dms) {
dmsLinks = regroupSwarm(population, getTopologyRange());
}
}
private boolean defaultsDone(AbstractEAIndividual indy) {
return (indy.hasData(partVelKey) && indy.hasData(indexKey));
}
/**
* Initialize individual defaults for the given population.
*
* @param pop
*/
protected void initDefaults(Population pop) {
AbstractEAIndividual indy;
for (int i = 0; i < pop.size(); i++) {
indy = pop.get(i);
if (indy instanceof InterfaceDataTypeDouble) {
if (!externalInitialPop || (!defaultsDone(indy))) {
initIndividualDefaults(indy, initialVelocity);
}
}
indy.putData(indexKey, i);
indy.setIndividualIndex(i);
}
}
/**
* Return the number of nodes of a complete n-ary tree with given branching
* factor and depth.
*
* @param branch
* @param depth
* @return
*/
public int getMaxNodes(int branch, int depth) {
int ret = (int) ((Math.pow(branch, depth + 1) - 1) / (branch - 1));
return ret;
}
/**
* This method will evaluate the current population using the given problem.
*
* @param population The population that is to be evaluated
*/
protected void evaluatePopulation(Population population) {
this.optimizationProblem.evaluate(population);
population.incrGeneration();
if (emaPeriods > 0) {
traceEMA(population);
}
}
public static void dumpPop(String prefix, Population pop) {
if (prefix != null) {
System.out.println(prefix);
}
for (int i = 0; i < pop.size(); i++) {
AbstractEAIndividual indy = pop.getEAIndividual(i);
String info = getParticleInfo(indy);
System.out.println(info);
}
}
public static String getParticleInfo(AbstractEAIndividual indy) {
String str = AbstractEAIndividual.getDefaultStringRepresentation(indy);
str += " / Vel: " + BeanInspector.toString(indy.getData(partVelKey));
str += " / BestP: " + BeanInspector.toString(indy.getData(partBestPosKey));
str += " / BestF: " + BeanInspector.toString(indy.getData(partBestFitKey));
str += " / PType: " + indy.getData(partTypeKey);
return str;
}
/**
* Compare the given attractor fitness value to the one remembered by the
* neighbour individual if useHistoric is true, else with the current
* fitness of the neighbour individual. If it is better than the attractor,
* overwrite the attractor with the neighbours information.
*
* @param attractorFit
* @param attractorPos
* @param neighbourIndy
* @param useHistoric
*/
protected void compareAndSetAttractor(double[] attractorFit, double[] attractorPos, AbstractEAIndividual neighbourIndy, boolean useHistoric) {
double[] neighbourFit;
double[] neighbourPos;
if (useHistoric) {
neighbourFit = (double[]) neighbourIndy.getData(partBestFitKey);
neighbourPos = (double[]) neighbourIndy.getData(partBestPosKey);
} else {
neighbourFit = neighbourIndy.getFitness();
neighbourPos = ((InterfaceDataTypeDouble) neighbourIndy).getDoubleData();
}
if (neighbourFit == null || attractorFit == null) {
System.err.println("error!");
}
// if (fit[0] >= tmpFit[0]) { // now multi-obj.
if (AbstractEAIndividual.isDominatingFitness(neighbourFit, attractorFit)) { // if the remembered fitness dominates the given one, overwrite the given one
// replace best fitness and position with current best
System.arraycopy(neighbourFit, 0, attractorFit, 0, neighbourFit.length);
System.arraycopy(neighbourPos, 0, attractorPos, 0, neighbourPos.length);
}
}
protected void resetIndividual(AbstractEAIndividual indy) {
resetIndividual(indy, initialVelocity);
plotIndy(((InterfaceDataTypeDouble) indy).getDoubleData(), null, (Integer) indy.getData(indexKey));
}
public static void resetIndividual(AbstractEAIndividual indy, double initialV) {
if (indy instanceof InterfaceDataTypeDouble) {
indy.setParents(null);
indy.defaultInit(null);
indy.putData(partTypeKey, defaultType); // turn into default type
initIndividualDefaults(indy, initialV);
initIndividualMemory(indy);
} else {
System.err.println("error, double valued individuals required for PSO");
}
}
/**
* This method will update a given individual according to the PSO method
*
* @param index The individual to update.
* @param pop The current population.
* @param best The best individual found so far.
*/
protected void updateIndividual(int index, AbstractEAIndividual indy, Population pop) {
if (indy instanceof InterfaceDataTypeDouble) {
int type = (Integer) indy.getData(partTypeKey);
switch (type) {
case resetType:
resetIndividual(indy);
break;
case defaultType:
defaultIndividualUpdate(index, indy, pop);
break;
default:
System.err.println("particle type " + type + " unknown!");
break;
}
} else {
throw new RuntimeException("Could not perform PSO update, because individual is not instance of InterfaceESIndividual!");
}
}
protected void defaultIndividualUpdate(int index, AbstractEAIndividual indy, Population pop) {
InterfaceDataTypeDouble endy = (InterfaceDataTypeDouble) indy;
indy.putData(partTypeKey, defaultType);
// default update
double[] personalBestPos = (double[]) indy.getData(partBestPosKey);
double[] velocity = (double[]) indy.getData(partVelKey);
double[] curPosition = endy.getDoubleData();
double[][] range = endy.getDoubleRange();
// search for the local best position
double[] neighbourBestPos = findNeighbourhoodOptimum(index, pop);
// now update the velocity
double[] curVelocity = updateVelocity(index, velocity, personalBestPos, curPosition, neighbourBestPos, range);
// check the speed limit
if (checkSpeedLimit) {
enforceSpeedLimit(curVelocity, range, getSpeedLimit(index));
}
// enforce range constraints if necessary
if (checkRange) {
ensureConstraints(curPosition, curVelocity, range);
}
plotIndy(curPosition, curVelocity, (Integer) indy.getData(indexKey));
// finally update the position
updatePosition(indy, curVelocity, curPosition, range);
resetFitness(indy);
}
protected void plotIndy(double[] curPosition, double[] curVelocity, int index) {
if (this.show) {
if (curVelocity == null) {
this.plot.setUnconnectedPoint(curPosition[0], curPosition[1], index);
} else {
this.plot.setConnectedPoint(curPosition[0], curPosition[1], index);
this.plot.setConnectedPoint(curPosition[0] + curVelocity[0], curPosition[1] + curVelocity[1], index);
}
}
}
/**
* Loop the population and update each individual props if indicated by
* isIndividualToUpdate.
*
* @param pop the population to work on
*/
protected void updateSwarmMemory(Population pop) {
for (int i = 0; i < pop.size(); i++) {
AbstractEAIndividual indy = pop.get(i);
if (isIndividualToUpdate(indy)) {
updateIndProps(indy, indy);
indy.putData(lastSuccessKey, indy.getData(partVelKey));
} else {
indy.putData(lastSuccessKey, null);
}
}
}
/**
* Reset the fitness of an individual the "hard" way.
*
* @param indy
*/
protected void resetFitness(AbstractEAIndividual indy) {
indy.resetFitness(0);
indy.resetConstraintViolation();
}
/**
* Write current fitness and position as best fitness and position into
* individual properties.
*
* @param srcIndy the individual to update
*/
protected void updateIndProps(AbstractEAIndividual trgIndy, AbstractEAIndividual srcIndy) {
trgIndy.putData(partBestFitKey, srcIndy.getFitness().clone());
trgIndy.putData(partBestPosKey, ((InterfaceDataTypeDouble) srcIndy).getDoubleData());
}
/**
* Return true if the given individual requires a property update, i.e. the
* current fitness is better than the best one seen so far.
*
* @param indy
* @return true if the given individual requires a property update
*/
protected boolean isIndividualToUpdate(AbstractEAIndividual indy) {
double[] bestFitness = (double[]) indy.getData(partBestFitKey);
return (AbstractEAIndividual.isDominatingFitnessNotEqual(indy.getFitness(), bestFitness));
}
/**
* Main velocity update step.
*
* @param index
* @param lastVelocity
* @param personalBestPos
* @param curPosition
* @param neighbourBestPos
* @param range
* @return
*/
protected double[] updateVelocity(int index, double[] lastVelocity, double[] personalBestPos, double[] curPosition, double[] neighbourBestPos, double[][] range) {
double[] accel, curVelocity = new double[lastVelocity.length];
if (useAlternative) {
accel = getAccelerationAlternative(index, personalBestPos, neighbourBestPos, curPosition, range);
} else {
accel = getAcceleration(personalBestPos, neighbourBestPos, curPosition, range);
}
for (int i = 0; i < lastVelocity.length; i++) {
curVelocity[i] = this.inertnessOrChi * lastVelocity[i];
curVelocity[i] += accel[i];
}
return curVelocity;
}
protected double[] getAcceleration(double[] personalBestPos, double[] neighbourBestPos, double[] curPosition, double[][] range) {
double[] accel = new double[curPosition.length];
double chi;
for (int i = 0; i < personalBestPos.length; i++) {
// the component from the old velocity
accel[i] = 0;
if (algType == PSOType.Constriction) {
chi = inertnessOrChi;
} else {
chi = 1.;
}
// the component from the cognition model
accel[i] = this.phi1 * chi * RNG.randomDouble(0, 1) * (personalBestPos[i] - curPosition[i]);
// the component from the social model
accel[i] += this.phi2 * chi * RNG.randomDouble(0, 1) * (neighbourBestPos[i] - curPosition[i]);
}
return accel;
}
protected double[] getAccelerationAlternative(int index, double[] personalBestPos, double[] neighbourBestPos, double[] curPosition, double[][] range) {
double[] accel = getAcceleration(personalBestPos, neighbourBestPos, curPosition, range);
double[] successfulVel = getSuccessfulVel(index);
if (successfulVel != null) {
Mathematics.vvAdd(accel, successfulVel, accel);
Mathematics.svMult(0.5, accel, accel);
}
return accel;
}
private double[] getSuccessfulVel(int index) {
if (true) {
return (double[]) population.getEAIndividual(index).getData(lastSuccessKey);
} else { // random one
ArrayList<Integer> successes = new ArrayList<>();
for (int i = 0; i < this.population.size(); i++) {
double[] succVel = (double[]) population.getEAIndividual(i).getData(lastSuccessKey);
if (succVel != null) {
successes.add(i);
}
}
if (successes.size() > 0) {
int i = successes.get(RNG.randomInt(successes.size()));
return (double[]) population.getEAIndividual(i).getData(lastSuccessKey);
} else {
return null;
}
}
}
/**
* Return a random vector after a gaussian distribution oriented along dir,
* meaning that variance is len(dir) along dir and len(dir)/scale in any
* other direction. If dir is a null-vector, having neither direction nor
* length, a null vector is returned.
*
* @param dir
* @return
*/
protected Matrix getOrientedGaussianRandomVectorB(Matrix dir, double scale) {
double len = dir.norm2();
int dim = dir.getRowDimension();
Matrix resVec = new Matrix(dim, 1);
Matrix randVec = new Matrix(dim, 1);
if (len > 0) {
// initialize random vector
// randVec.set(0, 0, len/2.+RNG.gaussianDouble(len/2));
// for (int i=1; i<dim; i++) {
// randVec.set(i, 0, RNG.gaussianDouble(len/(scale*2)));
// }
randVec.set(0, 0, project(0, len, len / 2 + RNG.gaussianDouble(len / 2)));
for (int i = 1; i < dim; i++) {
randVec.set(i, 0, project(-len / 2, len / 2, RNG.gaussianDouble(len / (scale * 2))));
}
Matrix rotation = Mathematics.getRotationMatrix(dir);
rotation = rotation.transpose();
//printMatrix(rotation);
resVec = rotation.times(randVec);
}
return resVec;
}
protected double[] getGaussianVector(double[] mean, double dev) {
double[] res = new double[mean.length];
RNG.gaussianVector(dev, res, false);
Mathematics.vvAdd(mean, res, res);
return res;
}
public static double project(double min, double max, double val) {
if (val < min) {
return min;
} else if (val > max) {
return max;
} else {
return val;
}
}
protected void printMatrix(Matrix M) {
for (int i = 0; i < M.getRowDimension(); i++) {
for (int j = 0; j < M.getColumnDimension(); j++) {
System.out.print(" " + M.get(i, j));
}
System.out.println("");
}
}
/**
* In the topology range for the given index, find the best stored
* individual and return its position.
*
* @param index index of the individual for which to check
* @param pop the current swarm
* @param best the currently best individual
* @return a copy of the position of the best remembered individual in the
* neigbourhood
*/
protected double[] findNeighbourhoodOptimum(int index, Population pop) {
double[] localBestPosition = null;
double[] localBestFitness = null;
int tmpIndex;
AbstractEAIndividual bestIndy, indy = pop.getEAIndividual(index);
boolean useHistoric = true;
int sortedIndex = -1;
int k;
localBestFitness = ((double[]) (indy).getData(partBestFitKey)).clone();
localBestPosition = ((double[]) (indy).getData(partBestPosKey)).clone();
if (useHistoric) {
bestIndy = bestIndividual;
} else {
bestIndy = pop.getBestEAIndividual();
}
switch (topology) {
case linear:
// linear
for (int x = -this.topologyRange; x <= this.topologyRange; x++) {
if (wrapTopology) {
tmpIndex = (index + x + pop.size()) % pop.size();
} else {
tmpIndex = index + x;
}
if ((x != 0) && (tmpIndex >= 0) && (tmpIndex < pop.size())) {
this.compareAndSetAttractor(localBestFitness, localBestPosition, pop.get(tmpIndex), useHistoric);
}
}
break;
case grid:
// grid
int corner = 1 + (int) Math.sqrt(pop.size());
for (int x = -this.topologyRange; x <= this.topologyRange; x++) {
for (int y = -this.topologyRange; y <= this.topologyRange; y++) {
tmpIndex = index + x + (y * corner);
if (wrapTopology) {
tmpIndex = (tmpIndex + pop.size()) % pop.size(); // wrap the grid toroidal
}
if ((x != index) && (tmpIndex >= 0) && (tmpIndex < pop.size())) {
this.compareAndSetAttractor(localBestFitness, localBestPosition, pop.get(tmpIndex), useHistoric);
}
}
}
break;
case star:
// star: only compare to the absolutely best
this.compareAndSetAttractor(localBestFitness, localBestPosition, bestIndy, useHistoric);
break;
case multiSwarm:
// self-organised multi-swarms
AbstractEAIndividual leader = (AbstractEAIndividual) indy.getData(multiSwTypeKey);
if (leader != null) { // refer to position of leader, this may be the individual itself
if ((leader == indy) && ((Integer) indy.getData(multiSwSizeKey) < minSubSwarmSize)) {
// swarm too small
this.compareAndSetAttractor(localBestFitness, localBestPosition, bestIndy, useHistoric);
//System.out.println("swarm too small.. using global attractor");
} else {
// if (!Arrays.equals(((InterfaceESIndividual)leader).getDGenotype(), ((InterfaceESIndividual)bestIndy).getDGenotype())) {
// System.out.println("leader is different from best");
// }
this.compareAndSetAttractor(localBestFitness, localBestPosition, leader, false);
}
} else {
// TODO handle this?
System.err.println("no leader present!");
}
break;
case tree: // Sorted Tree
sortedIndex = (Integer) ((AbstractEAIndividual) sortedPop[index]).getData(sortedIndexKey);
if (sortedIndex > 0) { // its found and its not the root. root has no parent to check for
k = getParentIndex(topologyRange, sortedIndex, pop.size());
compareAndSetAttractor(localBestFitness, localBestPosition, (AbstractEAIndividual) sortedPop[k], useHistoric);
}
if (treeStruct == 1) { // loop all children
if (isComplete(sortedIndex, pop.size())) { // the node has full degree
k = topologyRange * sortedIndex + 1; // this is the offset of the nodes children
for (int i = 0; i < topologyRange; i++) {
compareAndSetAttractor(localBestFitness, localBestPosition, (AbstractEAIndividual) sortedPop[k + i], useHistoric);
}
} else if (isIncomplete(sortedIndex, pop.size())) { // the node does not have full degree but might have orphans
int numOrphs = numOrphans(sortedIndex, pop.size());
if (numOrphs > 0) {
k = indexOfFirstOrphan(sortedIndex, pop.size());
for (int i = 0; i < numOrphs; i++) {
compareAndSetAttractor(localBestFitness, localBestPosition, (AbstractEAIndividual) sortedPop[k], useHistoric);
k += treeLastFullLevelNodeCnt; // hop to next (possible) orphan index
}
}
}
// this was the binary variant
// k = (2*sortedIndex+1);
// if (k < pop.size()) {
// compareAndSet(localBestFitness, localBestPosition, (AbstractEAIndividual)sortedPop[k], useHistoric);
// k++;
// if (k < pop.size()) compareAndSet(localBestFitness, localBestPosition, (AbstractEAIndividual)sortedPop[k], useHistoric);
// }
}
break;
case hpso: // Hierarchical PSO
if (index >= 0) {
k = getParentIndex(topologyRange, index, pop.size());
// compareAndSet(localBestFitness, localBestPosition, (AbstractEAIndividual)pop.get(k), useHistoric);
indy = pop.get(k);
System.arraycopy(indy.getData(partBestFitKey), 0, localBestFitness, 0, localBestFitness.length);
System.arraycopy(indy.getData(partBestPosKey), 0, localBestPosition, 0, localBestPosition.length);
}
break;
case random: // topologyRange random informants, may be the same several times
for (int i = 0; i < topologyRange; i++) {
// select random informant
indy = pop.get(RNG.randomInt(0, pop.size() - 1));
// set local values
compareAndSetAttractor(localBestFitness, localBestPosition, indy, useHistoric);
}
break;
case dms:
int groupIndex = (Integer) pop.getEAIndividual(index).getData(dmsGroupIndexKey);
int[] groupLinks = dmsLinks.get(groupIndex);
for (int i = 0; i < groupLinks.length; i++) {
if (groupLinks[i] != index) {
// select informant
indy = pop.getEAIndividual(groupLinks[i]);
// set local values
compareAndSetAttractor(localBestFitness, localBestPosition, indy, useHistoric);
}
}
break;
}
return localBestPosition;
}
/**
* Calculate a the parent index to a given index in a tree structure. The
* tree is complete except the last level, which is filled by assigning each
* parent of the last full level an orphan from left to right, so that no
* two parents differ in degree by more than one.
*
* @param branch
* @param index
* @param popSize
* @return
*/
protected int getParentIndex(int branch, int index, int popSize) {
int k;
if (isOrphan(index, popSize)) {
k = popSize - treeOrphans - treeLastFullLevelNodeCnt;
k += ((index - popSize + treeOrphans) % treeLastFullLevelNodeCnt); // index of the parent
} else {
k = (index - 1) / branch;
}
return k;
}
protected boolean isOrphan(int index, int popSize) {
return (index >= popSize - treeOrphans);
}
protected boolean isComplete(int index, int popSize) {
return (index < popSize - treeOrphans - treeLastFullLevelNodeCnt);
}
protected boolean isIncomplete(int index, int popSize) {
return ((index < popSize - treeOrphans) && (index >= popSize - treeOrphans - treeLastFullLevelNodeCnt));
}
/**
* Calculate the number of child orphans for tree nodes in the last internal
* layer (which is not complete, in the sense the nodes do not have full
* degree). Returns -1 if the indexed node is an orphan itself, or it is
* within a complete layer or if the index is out of range.
*
* @param index
* @param popSize
* @return
*/
protected int numOrphans(int index, int popSize) {
int k;
if (isIncomplete(index, popSize)) {
k = treeOrphans / treeLastFullLevelNodeCnt;
// if the index lies within orphans modulo lastFullCnt starting from lastFullLevel offset, there is one more child node
if ((index - (popSize - treeOrphans - treeLastFullLevelNodeCnt)) >= (treeOrphans % treeLastFullLevelNodeCnt)) {
return k;
} else {
return k + 1;
}
} else {
return -1;
}
}
protected int indexOfFirstOrphan(int index, int popSize) {
if (isIncomplete(index, popSize)) {
int k = popSize - treeOrphans + (index - (popSize - treeOrphans - treeLastFullLevelNodeCnt));
return k;
} else {
return -1;
}
}
/**
* Update the given individuals position with given speed and position,
* maybe perform checkBounds. Remember to reset the fitness and constraint
* violation of the individual.
*
* @param indy
* @param curVelocity
* @param curPosition
* @param range
*/
protected void updatePosition(AbstractEAIndividual indy, double[] curVelocity, double[] curPosition, double[][] range) {
double[] newPosition = new double[curPosition.length];
for (int i = 0; i < curPosition.length; i++) {
newPosition[i] = curPosition[i] + curVelocity[i];
}
if (checkRange && isOutOfRange(newPosition, range)) {
System.err.println("error, individual violates constraints!");
}
// finally set the new position and the current velocity
if (indy instanceof InterfaceDataTypeDouble) {
((InterfaceDataTypeDouble) indy).setDoubleGenotype(newPosition);
} else {
((InterfaceDataTypeDouble) indy).setDoubleGenotype(newPosition); // WARNING, this does a checkBounds in any case!
if (!checkRange) {
System.err.println("warning, checkbounds will be forced by InterfaceESIndividual!");
}
}
indy.putData(partVelKey, curVelocity);
// ((InterfaceESIndividual) indy).setDGenotype(newPosition);
}
/**
* Test a position for range violation efficiently.
*
* @param pos the position vector to test
* @param range the range array
* @return true, if pos violoates range, else false
*/
protected boolean isOutOfRange(double[] pos, double[][] range) {
boolean violatesRange = false;
for (int i = 0; i < pos.length; i++) {
if (!violatesRange) {
violatesRange = (pos[i] < range[i][0]) || (pos[i] > range[i][1]);
} else {
break;
}
}
return violatesRange;
}
/**
* Enforce the speed limit by repeatedly multiplying with the reduce-speed
* factor. Requires the range array because the speed is handled relative to
* the range.
*
* @param curVelocity the velocity vector to be modified
* @param range the range array
* @param speedLim the speed limit relative to the range
*/
protected void enforceSpeedLimit(double[] curVelocity, double[][] range, double speedLim) {
while (Mathematics.getRelativeLength(curVelocity, range) > speedLim) {
for (int i = 0; i < curVelocity.length; i++) {
curVelocity[i] *= this.reduceSpeed;
}
}
}
/**
* Makes sure that the future position (after adding velocity to pos)
* remains inside the range array. Therefore, the velocity may be repeatedly
* multiplied by reduceSpeedOnConstViolation.
*
* @param pos the current particle position
* @param velocity the current particle velocity to be modified
* @param range the range array
*/
protected void ensureConstraints(double[] pos, double[] velocity, double[][] range) {
double[] newPos = new double[pos.length];
for (int i = 0; i < pos.length; i++) {
newPos[i] = pos[i] + velocity[i];
}
if (isOutOfRange(pos, range)) {
System.err.println("warning, ensureConstraints called with already violating position (PSO)... reinitializing particle.");
for (int i = 0; i < pos.length; i++) {
if (!Mathematics.isInRange(pos[i], range[i][0], range[i][1])) {
pos[i] = RNG.randomDouble(range[i][0], range[i][1]);
}
}
}
for (int i = 0; i < pos.length; i++) {
if (!Mathematics.isInRange(newPos[i], range[i][0], range[i][1])) {
if ((pos[i] == range[i][0]) || (pos[i] == range[i][1])) {
// bounce?
velocity[i] *= reduceSpeedOnConstViolation; // bounce velocity and reduce
if (((pos[i] == range[i][0]) && (newPos[i] < range[i][0])) || ((pos[i] == range[i][1]) && (newPos[i] > range[i][1]))) {
velocity[i] *= -1; // bounce only if leaving in this direction.
}
newPos[i] = pos[i] + velocity[i];
} else {
// set vel. to land on the bounds
velocity[i] = (newPos[i] < range[i][0]) ? (range[i][0] - pos[i]) : (range[i][1] - pos[i]);
newPos[i] = pos[i] + velocity[i];
if ((newPos[i] < range[i][0]) || (newPos[i] > range[i][1])) {
velocity[i] *= .999; /// beware of floating point errors.
newPos[i] = pos[i] + velocity[i];
}
}
while ((newPos[i] < range[i][0]) || (newPos[i] > range[i][1])) {
//System.err.println("missed, pos was " + pos[i] + " vel was "+velocity[i]);
velocity[i] *= reduceSpeedOnConstViolation;
newPos[i] = pos[i] + velocity[i];
}
}
}
if (isOutOfRange(newPos, range)) {
System.err.println("narg, still out of range");
}
}
@Override
public void optimize() {
// System.out.println(">>> " + population.getStringRepresentation());
startOptimize();
// Update the individuals
updatePopulation();
// evaluate the population
this.evaluatePopulation(this.population);
// update the individual memory
updateSwarmMemory(population);
// log the best individual of the population
logBestIndividual();
// System.out.println("<<< " + population.getStringRepresentation());
// if (doLocalSearch && (population.getGeneration()%localSearchGens==0)) {
//// System.out.println("Local search at gen "+population.getGeneration());
// Population bestN = population.getBestNIndividuals(Math.max(1,(int)(lsCandidateRatio*population.size())));
//// Population bestN = population.getSortedNIndividuals(Math.max(1,(int)(lsCandidateRatio*population.size())), false);
// Population cands=(Population)bestN.clone();
// int maxSteps=cands.size()*lsStepsPerInd;
// int stepsDone = PostProcess.processSingleCandidates(PostProcessMethod.nelderMead, cands, maxSteps, 0.01, (AbstractOptimizationProblem)this.problem, null);
// for (int i=0; i<cands.size(); i++) {
// if (AbstractEAIndividual.isDominatingFitnessNotEqual(cands.getEAIndividual(i).getFitness(),
// (double[])bestN.getEAIndividual(i).getData(partBestFitKey))) {
//// System.out.println("Improved to " + BeanInspector.toString(cands.getEAIndividual(i).getFitness()) + " from " + BeanInspector.toString((double[])bestN.getEAIndividual(i).getData(partBestFitKey)));
// updateIndProps(bestN.getEAIndividual(i), cands.getEAIndividual(i));
// }
// }
// if (stepsDone>maxSteps) {
//// System.err.println("Warning: more steps performed than alloed in PSO LS: " + stepsDone + " vs. " + maxSteps);
// population.incrFunctionCallsBy(stepsDone);
// } else population.incrFunctionCallsBy(maxSteps);
// }
this.firePropertyChangedEvent(Population.NEXT_GENERATION_PERFORMED);
if (sleepTime > 0) {
try {
Thread.sleep(sleepTime);
} catch (Exception e) {
}
}
// maybeClearPlot();
}
protected void maybeClearPlot() {
if (((population.getGeneration() % 23) == 0) && isShow() && (plot != null)) {
plot.clearAll();
InterfaceDataTypeDouble indy = (InterfaceDataTypeDouble) this.population.get(0);
double[][] range = indy.getDoubleRange();
plot.setCornerPoints(range, 0);
}
}
/**
* Do some preparations in the beginning of the loop.
*/
protected void startOptimize() {
if (this.show) {
this.show();
}
sortedPop = null;// to make sure that the last sorted version is not reused
}
/**
* Log the best individual so far.
*/
protected void logBestIndividual() {
if (this.population.getBestEAIndividual().isDominatingDebConstraints(this.bestIndividual)) {
this.bestIndividual = (AbstractEAIndividual) this.population.getBestEAIndividual().clone();
this.bestIndividual.putData(partBestFitKey, this.bestIndividual.getFitness().clone());
this.bestIndividual.putData(partBestPosKey, ((InterfaceDataTypeDouble) this.bestIndividual).getDoubleData());
// System.out.println("new best: "+bestIndividual.toString());
}
}
/**
* Loop over the population and trigger the individual update.
*/
protected void updatePopulation() {
updateTopology(this.population);
for (int i = 0; i < this.population.size(); i++) {
this.updateIndividual(i, population.get(i), this.population);
}
if (show) {
if (this.optimizationProblem instanceof Interface2DBorderProblem) {
DPointSet popRep = new DPointSet();
InterfaceDataTypeDouble tmpIndy1;
double[] a = new double[2];
a[0] = 0.0;
a[1] = 0.0;
if (topoPlot == null) {
this.topoPlot = new TopoPlot("CBN-Species", "x", "y", a, a);
this.topoPlot.setParams(60, 60);
this.topoPlot.setTopology((Interface2DBorderProblem) this.optimizationProblem);
}
for (int i = 0; i < this.population.size(); i++) {
tmpIndy1 = (InterfaceDataTypeDouble) this.population.get(i);
popRep.addDPoint(new DPoint(tmpIndy1.getDoubleData()[0], tmpIndy1.getDoubleData()[1]));
}
this.topoPlot.getFunctionArea().addDElement(popRep);
//draw the species
}
}
}
protected void addSortedIndicesTo(Object[] sortedPopulation, Population pop) {
int origIndex;
for (int i = 0; i < pop.size(); i++) {
// cross-link the sorted list for faster access
origIndex = (Integer) ((AbstractEAIndividual) sortedPopulation[i]).getData(indexKey);
pop.get(origIndex).putData(sortedIndexKey, i);
}
}
protected void updateTopology(Population pop) {
if (topology == PSOTopology.dms) { // Dynamic multi-swarm after Liang & Suganthan
if (pop.getGeneration() % getDmsRegroupGens() == 0) {
dmsLinks = regroupSwarm(pop, getTopologyRange());
}
}
if ((topology == PSOTopology.multiSwarm) || (topology == PSOTopology.tree)) {
sortedPop = pop.toArray();
if ((topology == PSOTopology.multiSwarm) || (treeStruct >= 2)) {
Arrays.sort(sortedPop, new EAIndividualComparator());
} else {
Arrays.sort(sortedPop, new EAIndividualComparator(partBestFitKey));
}
addSortedIndicesTo(sortedPop, pop);
}
if (topology == PSOTopology.multiSwarm) {
// prepare multi swarm topology
PhenotypeMetric metric = new PhenotypeMetric();
Vector<AbstractEAIndividual> leaders = new Vector<>(pop.size());
int cur = 0;
boolean found = false, superfluous = false;
double dist;
while (cur < pop.size()) {
found = false;
superfluous = false;
for (int i = 0; i < leaders.size(); i++) {
dist = metric.distance((AbstractEAIndividual) sortedPop[cur], leaders.get(i));
//System.out.println("dist is "+dist);
if ((swarmRadius * 2.) > dist) { // a formal leader is found
int sSize = (Integer) (leaders.get(i)).getData(multiSwSizeKey);
if ((maxSubSwarmSize > 0) && (sSize >= maxSubSwarmSize)) {
// swarm is too big already
superfluous = true; // toggle reinitialize
found = true;
break;
} else {
found = true;
// assign to leader, update swarm size
((AbstractEAIndividual) sortedPop[cur]).putData(multiSwTypeKey, leaders.get(i));
((AbstractEAIndividual) sortedPop[cur]).putData(multiSwSizeKey, -1);
leaders.get(i).putData(multiSwSizeKey, 1 + sSize);
break;
}
}
}
if (!found) { // new leader is found
leaders.add(((AbstractEAIndividual) sortedPop[cur]));
((AbstractEAIndividual) sortedPop[cur]).putData(multiSwTypeKey, sortedPop[cur]);
((AbstractEAIndividual) sortedPop[cur]).putData(multiSwSizeKey, 1);
} else if (superfluous) {
//System.out.println("reinitializing " + cur);
((AbstractEAIndividual) sortedPop[cur]).putData(partTypeKey, resetType);
((AbstractEAIndividual) sortedPop[cur]).putData(multiSwTypeKey, sortedPop[cur]);
((AbstractEAIndividual) sortedPop[cur]).putData(multiSwSizeKey, 1);
}
cur++;
}
// for (int i=0; i<pop.size(); i++) { // this causes quadratic complexity. however, popsizes are usually small for pso, so its acceptable for testing
// if ((i!=index) && ((swarmRadius*2.)>metric.distance((AbstractEAIndividual)pop.get(i), (AbstractEAIndividual)pop.get(index)))) {
// this.compareAndSet(localBestFitness, localBestPosition, (AbstractEAIndividual)pop.get(i));
// }
// }
// for (int i=0; i<leaders.size(); i++) {
// int sSize = (Integer)(leaders.get(i)).getData(multiSwSizeKey);
// population.indexOf(leaders.get(i));
// System.out.print("s " + i + " w " + sSize + " (" + population.indexOf(leaders.get(i)) + "), ");
// }
//System.out.println(" -- best " + population.indexOf(population.getBestEAIndividual()));
}
if (topology == PSOTopology.hpso) { // HPSO sorting the population
int parentIndex;
AbstractEAIndividual indy;
EAIndividualComparator comp = new EAIndividualComparator(partBestFitKey);
for (int i = 0; i < pop.size(); i++) {
// loop over the part of the tree which is complete (full degree in each level)
parentIndex = getParentIndex(topologyRange, i, pop.size());
if (comp.compare(pop.get(i), pop.get(parentIndex)) < 0) { // sibling is dominant!
// so switch them
indy = pop.get(i);
pop.set(i, pop.get(parentIndex));
pop.set(parentIndex, indy);
}
}
// the old way (two loops) without good parentIndex function
// for (int i=0; i<pop.size()-treeOrphans; i++) {
// // loop over the part of the tree which is complete (full degree in each level)
// parentIndex=getParentIndex(branchDegree, i, pop.size());
// if (comp.compare(pop.get(i), pop.get(parentIndex)) < 0) { // sibling is dominant!
// // so switch them
// indy = (AbstractEAIndividual)pop.get(i);
// pop.set(i, pop.get(parentIndex));
// pop.set(parentIndex, indy);
// }
// }
// int nodesOfLastLevel = (int)Math.pow(branchDegree, treeLevels-1);
// // calc index of first parent of orphaned nodes
// parentIndex = pop.size()-treeOrphans-nodesOfLastLevel;
// int offset = pop.size()-treeOrphans;
// for (int i=0; i<treeOrphans; i++) {
// // now care about the orphans (last level)
// if (comp.compare(pop.get(offset+i), pop.get(parentIndex)) < 0) { // sibling is dominant!
// // so switch them
// indy = (AbstractEAIndividual)pop.get(offset+i);
// pop.set(offset+i, pop.get(parentIndex));
// pop.set(parentIndex, indy);
// }
// parentIndex++;
// if (parentIndex == offset) parentIndex = pop.size()-treeOrphans-nodesOfLastLevel;
// }
}
}
/**
* Randomly assign groups of size groupSize.
*
* @param pop
* @param groupSize
*/
private Vector<int[]> regroupSwarm(Population pop, int groupSize) {
int numGroups = pop.size() / groupSize; // truncated integer: last group is larger
int[] perm = RNG.randomPerm(pop.size());
Vector<int[]> links = new Vector<>(numGroups);
for (int i = 0; i < numGroups; i++) {
if (i < numGroups - 1) {
links.add(new int[groupSize]);
} else {
links.add(new int[pop.size() - (groupSize * i)]); // the last group is larger
}
int[] group = links.get(i);
for (int k = 0; k < group.length; k++) {
group[k] = perm[groupSize * i + k];
pop.getEAIndividual(group[k]).putData(dmsGroupIndexKey, i);
}
}
return links;
}
/**
* This method is simply for debugging.
*/
protected void show() {
if (this.plot == null) {
InterfaceDataTypeDouble indy = (InterfaceDataTypeDouble) this.population.get(0);
double[][] range = indy.getDoubleRange();
this.plot = new Plot("PSO " + population.getGeneration(), "x1", "x2", range[0], range[1]);
}
}
/**
* This method will return a string describing all properties of the
* optimizer and the applied methods.
*
* @return A descriptive string
*/
@Override
public String getStringRepresentation() {
String result = "";
result += "Particle Swarm Optimization:\n";
result += "Optimization Problem: ";
result += this.optimizationProblem.getStringRepresentationForProblem(this) + "\n";
result += this.population.getStringRepresentation();
return result;
}
/**
* This method will return a naming String
*
* @return The name of the algorithm
*/
@Override
public String getName() {
return "PSO-" + getTopology() + getTopologyRange() + "_" + getPhi1() + "_" + getPhi2();
}
@Override
public void setPopulation(Population pop) {
super.setPopulation(pop);
if (pop.size() > 0 && pop.size() != pop.getTargetSize()) { // new particle count!
tracedVelocity = null;
initializeByPopulation(null, false);
bestIndividual = pop.getBestEAIndividual();
} else {
for (int i = 0; i < pop.size(); i++) {
AbstractEAIndividual indy = pop.getEAIndividual(i);
if (indy == null) {
System.err.println("Error in PSO.setPopulation!");
} else if (!indy.hasData(this.partTypeKey)) {
initIndividualDefaults(indy, initialVelocity);
initIndividualMemory(indy);
indy.putData(indexKey, i);
indy.setIndividualIndex(i);
}
}
bestIndividual = pop.getBestEAIndividual();
}
}
@Override
public InterfaceSolutionSet getAllSolutions() {
return new SolutionSet(getPopulation());
}
public AbstractEAIndividual getBestIndividual() {
return bestIndividual;
}
/**
* This method will set the initial velocity
*
* @param f
*/
public void setInitialVelocity(double f) {
this.initialVelocity = f;
}
public double getInitialVelocity() {
return this.initialVelocity;
}
public String initialVelocityTipText() {
return "The initial velocity for each PSO particle.";
}
/**
* This method will set the speed limit
*
* @param k
*/
public void setSpeedLimit(double k) {
if (k < 0) {
k = 0;
}
if (k > 1) {
k = 1;
}
this.speedLimit = k;
}
public double getSpeedLimit() {
return this.speedLimit;
}
/**
* It may be useful to give each particle a different speed limit.
*
* @param index
* @return
*/
protected double getSpeedLimit(int index) {
return this.speedLimit;
}
public String speedLimitTipText() {
return "The speed limit in respect to the size of the search space [0,1].";
}
/**
* Set the phi values as well as the inertness parameter so as to resemble
* the constriction scheme. The constriction scheme calculates the speed
* update in the following way: v(t+1) = Chi * ( v(t) + tau1*u1*(p-x(t)) *
* tau2*u2*(g-x(t))) with u1, u2 random variables in (0,1) and tau1 and tau2
* usually set to 2.05. The sum tau1 and tau2 must be greater than 4. The
* Chi parameter (constriction) is set as in 2 Chi =
* ------------------------ |2-tau-sqrt(tau^2-4 tau)| where tau = tau1 +
* tau2 See Clerc&Kennedy: The Particle Swarm: Explosion, stability and
* convergence, 2002.
*
* @param tau1
* @param tau2
*/
protected void setConstriction(double tau1, double tau2) {
double pSum = tau1 + tau2;
if (pSum <= 4) {
System.err.println("error, invalid tauSum value in PSO::setConstriction");
} else {
if (getAlgoType() != PSOType.Constriction) {
System.err.println("Warning, PSO algorithm variant constriction expected!");
}
phi1 = tau1;
phi2 = tau2;
setInertnessOrChi(2. / (Math.abs(2 - pSum - Math.sqrt((pSum * pSum) - (4 * pSum)))));
}
}
/**
* This method will set the inertness/chi value
*
* @param k
*/
public void setInertnessOrChi(double k) {
this.inertnessOrChi = k;
}
public double getInertnessOrChi() {
return this.inertnessOrChi;
}
public String inertnessOrChiTipText() {
return "Gives the speed decay of the previous velocity [0,1] in inertness mode or the chi value in constriction mode which is calculated from phi1 and phi2.";
}
/**
* This method will set greediness to move towards the best cognition
*
* @param l
*/
@Parameter(description = "Acceleration for the cognition model.")
public void setPhi1(double l) {
this.phi1 = l;
if (algType == PSOType.Constriction) {
setConstriction(getPhi1(), getPhi2());
}
}
public double getPhi1() {
return this.phi1;
}
/**
* This method will set greediness to move towards the best social
*
* @param l
*/
@Parameter(description = "Acceleration for the social model.")
public void setPhi2(double l) {
this.phi2 = l;
if (algType == PSOType.Constriction) {
setConstriction(getPhi1(), getPhi2());
}
}
public double getPhi2() {
return this.phi2;
}
/**
* Set the phi1 / phi2 parameter values (and in the constriction variant,
* adapt constriction factor).
*
* @param phi1
* @param phi2
*/
public void setPhiValues(double phi1, double phi2) {
this.phi1 = phi1;
this.phi2 = phi2;
if (algType == PSOType.Constriction) {
setConstriction(phi1, phi2);
}
}
/**
* Directly set all parameter values phi1, phi2 and inertness/constriction
* factor.
*
* @param phi1
* @param phi2
* @param inertness
*/
public void setParameterValues(double phi1, double phi2, double inertness) {
this.phi1 = phi1;
this.phi2 = phi2;
setInertnessOrChi(inertness);
}
/**
* This method allows you to choose the topology type.
*
* @param t The type.
*/
@Parameter(description = "Choose the topology type")
public void setTopology(PSOTopology t) {
this.topology = t;
setGOEShowProperties(getClass());
}
public void setGOEShowProperties(Class<?> cls) {
// linear, grid, random
GenericObjectEditor.setShowProperty(cls, "topologyRange", (topology == PSOTopology.linear) || (topology == PSOTopology.grid) || (topology == PSOTopology.random) || (topology == PSOTopology.tree) || (topology == PSOTopology.hpso) || (topology == PSOTopology.dms));
// multi swarm
GenericObjectEditor.setShowProperty(cls, "subSwarmRadius", (topology == PSOTopology.multiSwarm));
// multi swarm
GenericObjectEditor.setShowProperty(cls, "maxSubSwarmSize", (topology == PSOTopology.multiSwarm));
// tree
GenericObjectEditor.setShowProperty(cls, "treeStruct", (topology == PSOTopology.tree));
// tree, hpso
// GenericObjectEditor.setShowProperty(cls, "treeBranchDegree", (topology==PSOTopologyEnum.tree) || (topology==PSOTopologyEnum.hpso));
// linear
GenericObjectEditor.setShowProperty(cls, "wrapTopology", (topology == PSOTopology.linear) || (topology == PSOTopology.grid));
// dms
GenericObjectEditor.setShowProperty(cls, "dmsRegroupGens", (topology == PSOTopology.dms));
}
public PSOTopology getTopology() {
return topology;
}
/**
* This method allows you to choose the algorithm type.
*
* @param s The type.
*/
public void setAlgoType(PSOType s) {
this.algType = s;
if (algType == PSOType.Constriction) {
setConstriction(getPhi1(), getPhi2());
}
}
@Parameter(description = "Choose the inertness or constriction method. Chi is calculated automatically in constriction.")
public PSOType getAlgoType() {
return this.algType;
}
/**
* The range of the local neighbourhood.
*
* @param s The range.
*/
@Parameter(description = "The range of the neighborhood topology.")
public void setTopologyRange(int s) {
this.topologyRange = s;
}
public int getTopologyRange() {
return this.topologyRange;
}
/**
* Toggle Check Constraints.
*
* @param s Check Constraints.
*/
@Parameter(description = "Toggle whether particles are allowed to leave the range.")
public void setCheckRange(boolean s) {
this.checkRange = s;
}
public boolean isCheckRange() {
return this.checkRange;
}
/**
* @return true if swarm visualization is turned on
*/
public boolean isShow() {
return show;
}
/**
* @param set swarm visualization (2D)
*/
public void setShow(boolean show) {
this.show = show;
if (!show) {
plot = null;
}
}
public String showTipText() {
return "Activate for debugging in 2D";
}
/**
* @return the checkSpeedLimit
*/
public boolean isCheckSpeedLimit() {
return checkSpeedLimit;
}
/**
* @param checkSpeedLimit the checkSpeedLimit to set
*/
public void setCheckSpeedLimit(boolean checkSpeedLimit) {
this.checkSpeedLimit = checkSpeedLimit;
GenericObjectEditor.setHideProperty(getClass(), "speedLimit", !checkSpeedLimit);
}
public String checkSpeedLimitTipText() {
return "if activated, the speed limit is enforced for the particles";
}
/**
* @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 "Sleep for a time between iterations - to be used with debugging and the show option.";
}
public double getSubSwarmRadius() {
return swarmRadius;
}
public void setSubSwarmRadius(double radius) {
swarmRadius = radius;
}
public String subSwarmRadiusTipText() {
return "Define the maximum distance to a swarm leader in the multi-swarm variant";
}
public int getMaxSubSwarmSize() {
return maxSubSwarmSize;
}
public void setMaxSubSwarmSize(int subSize) {
maxSubSwarmSize = subSize;
}
public String maxSubSwarmSizeTipText() {
return "Maximum size of a sub swarm. Violating particles will be reinitialized. 0 means no limit to the sub swarm size.";
}
public int getTreeStruct() {
return treeStruct;
}
public void SetTreeStruct(int treeStruct) {
this.treeStruct = treeStruct;
}
public boolean isWrapTopology() {
return wrapTopology;
}
public void setWrapTopology(boolean wrapTopology) {
this.wrapTopology = wrapTopology;
}
public String wrapTopologyTipText() {
return "Wraps the topology to a ring structure";
}
protected int getEmaPeriods() {
return emaPeriods;
}
protected void setEmaPeriods(int emaPeriods) {
this.emaPeriods = emaPeriods;
}
/**
* 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.";
}
/**
* Retrieve the set of personal best positions contained in the given
* population.
*
* @param population
* @return
*/
protected Population getPersonalBestPos(Population population) {
Population bests = new Population(population.size());
AbstractEAIndividual indy = (AbstractEAIndividual) population.getEAIndividual(0).clone();
if (!indy.hasData(partBestFitKey)) {
return null;
}
for (int i = 0; i < population.size(); i++) {
double[] personalBestPos = (double[]) population.getEAIndividual(i).getData(partBestPosKey);
double[] personalBestfit = (double[]) population.getEAIndividual(i).getData(partBestFitKey);
double relDiff;
if (personalBestfit[0] != 0) {
relDiff = (personalBestfit[0] - ((InterfaceProblemDouble) optimizationProblem).evaluate(personalBestPos)[0]) / personalBestfit[0];
} else {
relDiff = (personalBestfit[0] - ((InterfaceProblemDouble) optimizationProblem).evaluate(personalBestPos)[0]); // absolute diff in this case
}// if (personalBestfit[0]!=((InterfaceProblemDouble)problem).evaluate(personalBestPos)[0]) {
if (Math.abs(relDiff) > 1e-20) {
System.err.println("Warning: mismatching best fitness by " + relDiff);
System.err.println("partInfo: " + i + " - " + getParticleInfo(population.getEAIndividual(i)));
}
if (Math.abs(relDiff) > 1e-10) {
System.err.println("partInfo: " + i + " - " + getParticleInfo(population.getEAIndividual(i)));
throw new RuntimeException("Mismatching best fitness!! " + personalBestfit[0] + " vs. " + ((InterfaceProblemDouble) optimizationProblem).evaluate(personalBestPos)[0]);
}
((InterfaceDataTypeDouble) indy).setDoubleGenotype(personalBestPos);
indy.setFitness(personalBestfit);
bests.add((AbstractEAIndividual) indy.clone());
}
return bests;
}
public int getDmsRegroupGens() {
return dmsRegroupInterval;
}
public void setDmsRegroupGens(int dmsRegroupInterval) {
this.dmsRegroupInterval = dmsRegroupInterval;
}
public String dmsRegroupGensTipText() {
return "The number of generations after which new subswarms are randomly formed.";
}
@Override
public String[] getAdditionalDataHeader() {
if (emaPeriods > 0) {
return new String[]{"meanEMASpeed", "meanCurSpeed"};
} else {
return new String[]{"meanCurSpeed"};
}
}
@Override
public String[] getAdditionalDataInfo() {
if (emaPeriods > 0) {
return new String[]{"Exponential moving average of the (range-relative) speed of all particles", "The mean (range-relative) current speed of all particles"};
} else {
return new String[]{"The mean (range-relative) current speed of all particles"};
}
}
@Override
public Object[] getAdditionalDataValue(PopulationInterface pop) {
AbstractEAIndividual indy = (AbstractEAIndividual) pop.get(0);
if (emaPeriods > 0) {
double relSp;
if (indy instanceof InterfaceDataTypeDouble) {
relSp = getRelativeEMASpeed(((InterfaceDataTypeDouble) indy).getDoubleRange());
} else {
relSp = Double.NaN;
}
return new Object[]{relSp, getPopulationAvgNormedVelocity((Population) pop)};
} else {
return new Object[]{getPopulationAvgNormedVelocity((Population) pop)};
}
}
}