package holeg.algorithm.topologie; import holeg.algorithm.objective_function.TopologieObjectiveFunction; import holeg.api.TopologieAlgorithmFramework; import holeg.model.Model; import holeg.utility.math.Maths; import holeg.utility.math.Random; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.TreeSet; import java.util.stream.Collectors; public class PsoAlgorithm extends TopologieAlgorithmFramework { private int popsize = 20; private int maxGenerations = 500; private double dependency = 2.07; private double c1, c2, w; private double maxVelocity = 10.0; private double deviation = 0.5; //mutation private int mutationInterval = 1; private boolean useIntervalMutation = false; private double mutationRate = 0.005; private double mutateProbabilityInterval = 0.01; private double maxMutationPercent = 0.01; private boolean moreInformation = false; public PsoAlgorithm() { addIntParameter("Swarmsize", popsize, intValue -> popsize = intValue, () -> popsize, 1); addIntParameter("Iterations", maxGenerations, intValue -> maxGenerations = intValue, () -> maxGenerations, 1); addSeperator(); addDoubleParameter("Deviation", deviation, doubleValue -> deviation = doubleValue, () -> deviation, 0); addDoubleParameter("Dependency", dependency, doubleValue -> dependency = doubleValue, () -> dependency, true, 2.001, 2.4); addDoubleParameter("Particle Max-Velocity", maxVelocity, doubleValue -> maxVelocity = doubleValue, () -> maxVelocity, 0.0); addSeperator(); addIntParameter("Mutation Frequency (Iteration)", mutationInterval, intValue -> mutationInterval = intValue, () -> mutationInterval, 0); addBooleanParameter("Use Interval Mutation", useIntervalMutation, booleanValue -> useIntervalMutation = booleanValue, Arrays.asList("Probability for Frequency Mutation", "Scope of Mutation"), Arrays.asList("Probability for Bit-wise Mutation")); addDoubleParameter("Probability for Frequency Mutation", mutateProbabilityInterval, doubleValue -> mutateProbabilityInterval = doubleValue, () -> mutateProbabilityInterval, useIntervalMutation, 0.0, 1.0); addDoubleParameter("Probability for Bit-wise Mutation", mutationRate, doubleValue -> mutationRate = doubleValue, () -> mutationRate, !useIntervalMutation, 0.0, 1.0); addDoubleParameter("Scope of Mutation", maxMutationPercent, doubleValue -> maxMutationPercent = doubleValue, () -> maxMutationPercent, useIntervalMutation, 0.0, 1.0); addSeperator(); addBooleanParameter("Print Auxiliary Information", moreInformation, booleanValue -> moreInformation = booleanValue, new LinkedList(), new LinkedList()); } @Override protected double evaluateState(Model model, int amountOfAddedSwitch, double addedCableMeters, boolean moreInformation) { return TopologieObjectiveFunction.getFitnessValueForState(model, amountOfAddedSwitch, addedCableMeters, moreInformation); } @Override /** *

Algo from Paper:

   *
   *  Begin
   *  	t = 0; {t: generation index}
   *  	initialize particles xp,i,j(t);
   *  	evaluation xp,i,j(t);
   *  	while (termination condition ≠ true) do
   *  		vi,j(t) = update vi,j(t); {by Eq. (6)}
   *  		xg,i,j(t) = update xg,i,j(t); {by Eq. (7)}
   *  		xg,i,j(t) = mutation xg,i,j(t); {by Eq. (11)}
   *  		xp,i,j(t) = decode xg,i,j(t); {by Eqs. (8) and (9)}
   *  		evaluate xp,i,j(t);
   *  		t = t + 1;
   *  	end while
   *  End
*

with:

* * xg,i,j: genotype ->genetic information -> in continuous space
* xp,i,j: phenotype -> observable characteristics-> in binary space
* Xg,max: is the Maximum here set to 4.
* Eq. (6):vi,j(t + 1) = wvi,j+c1R1(Pbest,i,j-xp,i,j(t))+c2R2(gbest,i,j-xp,i,j(t))
* Eq. (7):xg,i,j(t + 1) = xg,i,j(t) + vi,j(t + 1)
* Eq. (11):if(rand()<rmu)then xg,i,j(t + 1) = -xg,i,j(t + 1)
* Eq. (8):xp,i,j(t + 1) = (rand() < S(xg,i,j(t + 1))) ? 1 : 0
* Eq. (9) Sigmoid:S(xg,i,j(t + 1)) := 1/(1 + e-xg,i,j(t + 1))
*

Parameter:

* w inertia, calculated from phi(Variable:{@link #dependency})
* c1: influence, calculated from phi(Variable:{@link #dependency})
* c2: influence, calculated from phi(Variable:{@link #dependency})
* rmu: probability that the proposed operation is conducted defined by limit(Variable:{@link #limit})
* * */ protected Individual executeAlgo() { resetWildcards(); initDependentParameter(); Individual globalBest = new Individual(); globalBest.position = extractPositionAndAccess(); globalBest.fitness = evaluatePosition(globalBest.position); console.println("Start Value:" + globalBest.fitness); int dimensions = globalBest.position.size(); List swarm = initializeParticles(dimensions); List runList = new ArrayList(); runList.add(globalBest.fitness); evaluation(globalBest, swarm); runList.add(globalBest.fitness); for (int iteration = 0; iteration < this.maxGenerations; iteration++) { int mutationAllowed = iteration % mutationInterval; double bitsFlipped = 0; for (int particleNumber = 0; particleNumber < this.popsize; particleNumber++) { Particle particle = swarm.get(particleNumber); if (this.useIntervalMutation) { boolean allowMutation = (Random.nextDouble() < this.mutateProbabilityInterval); TreeSet mutationLocationSet = null; if (allowMutation) { mutationLocationSet = locationsToMutate(dimensions); } for (int index = 0; index < dimensions; index++) { updateVelocity(particle, index, globalBest); updateGenotype(particle, index); if (allowMutation && mutationAllowed == 0 && iteration != 0 && mutationLocationSet.contains(index)) { mutation(particle, index); } decode(particle, index); } } else { int count = 0; for (int index = 0; index < dimensions; index++) { updateVelocity(particle, index, globalBest); updateGenotype(particle, index); if (mutationAllowed == 0 && iteration != 0 && Random.nextDouble() < mutationRate) { count++; mutation(particle, index); } decode(particle, index); } bitsFlipped += count; } } if (moreInformation) { console.println("\t\t\t\t\t\tAvgBitsMutate: " + (bitsFlipped / (double) popsize)); } if (cancel) { return null; } evaluation(globalBest, swarm); runList.add(globalBest.fitness); if (moreInformation) { console.println("------------------------"); } } console.println(" End Value:" + globalBest.fitness); this.runList = runList; return globalBest; } @Override protected int getProgressBarMaxCount() { return rounds * maxGenerations * popsize + 1; } @Override protected String algoInformationToPrint() { return "PsoAlgo" + " Rounds:" + rounds + " maxIterations:" + maxGenerations + " swarmSize:" + popsize + " dependency:" + dependency + " mutationInterval:" + mutationInterval + " maxVelocity: " + maxVelocity + " deviation: " + deviation + (useIntervalMutation ? (" mutateProbabilityInterval:" + mutateProbabilityInterval + " maxMutationPercent:" + maxMutationPercent) : " mutationRate:" + mutationRate); } @Override protected String plottFileName() { return "pso-topologie.txt"; } /** * @param problemSize maximum index of position in the particle * @return */ private List initializeParticles(int problemSize) { List swarm = new ArrayList(); //Create The Particle for (int particleNumber = 0; particleNumber < popsize; particleNumber++) { //Create a Random position List aRandomPosition = new ArrayList(); for (int index = 0; index < problemSize; index++) { aRandomPosition.add(Random.nextIntegerInRange(0, this.getMaximumIndexObjects(index) + 1)); } swarm.add(new Particle(aRandomPosition)); } return swarm; } /** * Calculate w, c1, c2 */ private void initDependentParameter() { w = 1.0 / (dependency - 1 + Math.sqrt(dependency * dependency - 2 * dependency)); c1 = c2 = dependency * w; } /** * Evaluate each particle and update the global Best position; * * @param globalBest * @param swarm */ private void evaluation(Individual globalBest, List swarm) { for (Particle p : swarm) { double localEvaluationValue = evaluatePosition(p.xPhenotype); if (moreInformation) { console.println("Fitness " + localEvaluationValue); } p.checkNewEvaluationValue(localEvaluationValue); if (localEvaluationValue < globalBest.fitness) { globalBest.fitness = localEvaluationValue; globalBest.position = p.localBest.position; } } } /** * Eq. (6):vi,j(t + 1) = wvi,j+c1R1(Pbest,i,j-xp,i,j(t))+c2R2(gbest,i,j-xp,i,j(t))
* * @param particle * @param index * @param globalBest */ private void updateVelocity(Particle particle, int index, Individual globalBest) { double r1 = Random.nextDouble(); double r2 = Random.nextDouble(); double posValue = particle.xPhenotype.get(index); particle.velocity.set(index, clamp( w * particle.velocity.get(index) + c1 * r1 * ((particle.localBest.position.get(index)) - posValue) + c2 * r2 * ((globalBest.position.get(index)) - posValue))); } /** * Eq. (7):xg,i,j(t + 1) = xg,i,j(t) + vi,j(t + 1)
* * @param particle * @param index */ private void updateGenotype(Particle particle, int index) { particle.xGenotype.set(index, clamp(particle.xGenotype.get(index) + particle.velocity.get(index))); } /** * Eq. (11):if(rand()<rmu)then xg,i,j(t + 1) = * -xg,i,j(t + 1)
* * @param particle * @param index */ private void mutation(Particle particle, int index) { //if(Random.nextDouble() < limit) particle.xGenotype.set(index, -particle.xGenotype.get(index)); } /** * Eq. (8):xp,i,j(t + 1) = (rand() < S(xg,i,j(t + 1))) ? 1 * : 0
* * @param particle * @param index */ private void decode(Particle particle, int index) { double value = clamp(Random.nextGaussian(particle.xGenotype.get(index), deviation)); double alpha = Maths.invLerp(-maxVelocity, +maxVelocity, value); double result = Maths.lerp(0, this.getMaximumIndexObjects(index), alpha); particle.xPhenotype.set(index, (int) Math.round(result)); } /** * To clamp Xg,j,i and vi,j in Range [-Xg,max|+Xg,max] * with {Xg,max= 4} * * @param value * @return */ private double clamp(double value) { return Math.max(-maxVelocity, Math.min(maxVelocity, value)); } private TreeSet locationsToMutate(int dimensions) { TreeSet mutationLocation = new TreeSet(); //sortedSet int maximumAmountOfMutatedBits = Math.max(1, (int) Math.round(((double) dimensions) * this.maxMutationPercent)); int randomUniformAmountOfMutatedValues = Random.nextIntegerInRange(1, maximumAmountOfMutatedBits + 1); for (int i = 0; i < randomUniformAmountOfMutatedValues; i++) { boolean success = mutationLocation.add(Random.nextIntegerInRange(0, dimensions)); if (!success) { i--; //can be add up to some series long loops if maximumAmountOfMutatedBits get closed to problemsize. } } return mutationLocation; } /** * Class to represent a Particle. */ private class Particle { /** * The velocity of a particle. */ public List velocity; /** * The positions genotype. */ public List xGenotype; /** * The positions phenotype. Alias the current position. */ public List xPhenotype; public Individual localBest; Particle(List position) { this.xPhenotype = position; //Init velocity, xGenotype with 0.0 values. this.velocity = position.stream().map(bool -> 0.0).collect(Collectors.toList()); this.xGenotype = position.stream().map(bool -> 0.0).collect(Collectors.toList()); localBest = new Individual(); localBest.fitness = Double.MAX_VALUE; } public void checkNewEvaluationValue(double newEvaluationValue) { if (newEvaluationValue < localBest.fitness) { localBest.fitness = newEvaluationValue; localBest.position = xPhenotype.stream().collect(Collectors.toList()); } } public String toString() { return "Particle with xPhenotype(Position), xGenotype, velocity:[" + listToString(xPhenotype) + "],[" + listToString(xGenotype) + "],[" + listToString(velocity) + "]"; } private String listToString(List list) { return list.stream().map(Object::toString).collect(Collectors.joining(", ")); } } }