PsoAlgorithm.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. package holeg.algorithm.topologie;
  2. import holeg.algorithm.objective_function.TopologieObjectiveFunction;
  3. import holeg.api.TopologieAlgorithmFramework;
  4. import holeg.model.Model;
  5. import holeg.utility.math.Maths;
  6. import holeg.utility.math.Random;
  7. import java.util.ArrayList;
  8. import java.util.Arrays;
  9. import java.util.LinkedList;
  10. import java.util.List;
  11. import java.util.TreeSet;
  12. import java.util.stream.Collectors;
  13. public class PsoAlgorithm extends TopologieAlgorithmFramework {
  14. private int popsize = 20;
  15. private int maxGenerations = 500;
  16. private double dependency = 2.07;
  17. private double c1, c2, w;
  18. private double maxVelocity = 10.0;
  19. private double deviation = 0.5;
  20. //mutation
  21. private int mutationInterval = 1;
  22. private boolean useIntervalMutation = false;
  23. private double mutationRate = 0.005;
  24. private double mutateProbabilityInterval = 0.01;
  25. private double maxMutationPercent = 0.01;
  26. private boolean moreInformation = false;
  27. public PsoAlgorithm() {
  28. addIntParameter("Swarmsize", popsize, intValue -> popsize = intValue, () -> popsize, 1);
  29. addIntParameter("Iterations", maxGenerations, intValue -> maxGenerations = intValue,
  30. () -> maxGenerations, 1);
  31. addSeperator();
  32. addDoubleParameter("Deviation", deviation, doubleValue -> deviation = doubleValue,
  33. () -> deviation, 0);
  34. addDoubleParameter("Dependency", dependency, doubleValue -> dependency = doubleValue,
  35. () -> dependency, true, 2.001, 2.4);
  36. addDoubleParameter("Particle Max-Velocity", maxVelocity,
  37. doubleValue -> maxVelocity = doubleValue, () -> maxVelocity, 0.0);
  38. addSeperator();
  39. addIntParameter("Mutation Frequency (Iteration)", mutationInterval,
  40. intValue -> mutationInterval = intValue, () -> mutationInterval, 0);
  41. addBooleanParameter("Use Interval Mutation", useIntervalMutation,
  42. booleanValue -> useIntervalMutation = booleanValue,
  43. Arrays.asList("Probability for Frequency Mutation", "Scope of Mutation"),
  44. Arrays.asList("Probability for Bit-wise Mutation"));
  45. addDoubleParameter("Probability for Frequency Mutation", mutateProbabilityInterval,
  46. doubleValue -> mutateProbabilityInterval = doubleValue, () -> mutateProbabilityInterval,
  47. useIntervalMutation, 0.0, 1.0);
  48. addDoubleParameter("Probability for Bit-wise Mutation", mutationRate,
  49. doubleValue -> mutationRate = doubleValue, () -> mutationRate, !useIntervalMutation, 0.0,
  50. 1.0);
  51. addDoubleParameter("Scope of Mutation", maxMutationPercent,
  52. doubleValue -> maxMutationPercent = doubleValue, () -> maxMutationPercent,
  53. useIntervalMutation, 0.0, 1.0);
  54. addSeperator();
  55. addBooleanParameter("Print Auxiliary Information", moreInformation,
  56. booleanValue -> moreInformation = booleanValue, new LinkedList<String>(),
  57. new LinkedList<String>());
  58. }
  59. @Override
  60. protected double evaluateState(Model model, int amountOfAddedSwitch, double addedCableMeters,
  61. boolean moreInformation) {
  62. return TopologieObjectiveFunction.getFitnessValueForState(model, amountOfAddedSwitch,
  63. addedCableMeters, moreInformation);
  64. }
  65. @Override
  66. /**
  67. * <p>Algo from Paper:</p><font size="3"><pre>
  68. *
  69. * Begin
  70. * t = 0; {t: generation index}
  71. * initialize particles x<sub>p,i,j</sub>(t);
  72. * evaluation x<sub>p,i,j</sub>(t);
  73. * while (termination condition &ne; true) do
  74. * v<sub>i,j</sub>(t) = update v<sub>i,j</sub>(t); {by Eq. (6)}
  75. * x<sub>g,i,j</sub>(t) = update x<sub>g,i,j</sub>(t); {by Eq. (7)}
  76. * x<sub>g,i,j</sub>(t) = mutation x<sub>g,i,j</sub>(t); {by Eq. (11)}
  77. * x<sub>p,i,j</sub>(t) = decode x<sub>g,i,j</sub>(t); {by Eqs. (8) and (9)}
  78. * evaluate x<sub>p,i,j</sub>(t);
  79. * t = t + 1;
  80. * end while
  81. * End</pre></font>
  82. * <p>with:</p><font size="3">
  83. *
  84. * x<sub>g,i,j</sub>: genotype ->genetic information -> in continuous space<br>
  85. * x<sub>p,i,j</sub>: phenotype -> observable characteristics-> in binary space<br>
  86. * X<sub>g,max</sub>: is the Maximum here set to 4.<br>
  87. * Eq. (6):v<sub>i,j</sub>(t + 1) = wv<sub>i,j</sub>+c<sub>1</sub>R<sub>1</sub>(P<sub>best,i,j</sub>-x<sub>p,i,j</sub>(t))+c<sub>2</sub>R<sub>2</sub>(g<sub>best,i,j</sub>-x<sub>p,i,j</sub>(t))<br>
  88. * Eq. (7):x<sub>g,i,j</sub>(t + 1) = x<sub>g,i,j</sub>(t) + v<sub>i,j</sub>(t + 1)<br>
  89. * Eq. (11):<b>if(</b>rand()&lt;r<sub>mu</sub><b>)then</b> x<sub>g,i,j</sub>(t + 1) = -x<sub>g,i,j</sub>(t + 1)<br>
  90. * Eq. (8):x<sub>p,i,j</sub>(t + 1) = <b>(</b>rand() &lt; S(x<sub>g,i,j</sub>(t + 1))<b>) ?</b> 1 <b>:</b> 0<br>
  91. * Eq. (9) Sigmoid:S(x<sub>g,i,j</sub>(t + 1)) := 1/(1 + e<sup>-x<sub>g,i,j</sub>(t + 1)</sup>)<br></font>
  92. * <p>Parameter:</p>
  93. * w inertia, calculated from phi(Variable:{@link #dependency})<br>
  94. * c1: influence, calculated from phi(Variable:{@link #dependency}) <br>
  95. * c2: influence, calculated from phi(Variable:{@link #dependency})<br>
  96. * r<sub>mu</sub>: probability that the proposed operation is conducted defined by limit(Variable:{@link #limit})<br>
  97. *
  98. *
  99. */
  100. protected Individual executeAlgo() {
  101. resetWildcards();
  102. initDependentParameter();
  103. Individual globalBest = new Individual();
  104. globalBest.position = extractPositionAndAccess();
  105. globalBest.fitness = evaluatePosition(globalBest.position);
  106. console.println("Start Value:" + globalBest.fitness);
  107. int dimensions = globalBest.position.size();
  108. List<Particle> swarm = initializeParticles(dimensions);
  109. List<Double> runList = new ArrayList<Double>();
  110. runList.add(globalBest.fitness);
  111. evaluation(globalBest, swarm);
  112. runList.add(globalBest.fitness);
  113. for (int iteration = 0; iteration < this.maxGenerations; iteration++) {
  114. int mutationAllowed = iteration % mutationInterval;
  115. double bitsFlipped = 0;
  116. for (int particleNumber = 0; particleNumber < this.popsize; particleNumber++) {
  117. Particle particle = swarm.get(particleNumber);
  118. if (this.useIntervalMutation) {
  119. boolean allowMutation = (Random.nextDouble() < this.mutateProbabilityInterval);
  120. TreeSet<Integer> mutationLocationSet = null;
  121. if (allowMutation) {
  122. mutationLocationSet = locationsToMutate(dimensions);
  123. }
  124. for (int index = 0; index < dimensions; index++) {
  125. updateVelocity(particle, index, globalBest);
  126. updateGenotype(particle, index);
  127. if (allowMutation && mutationAllowed == 0 && iteration != 0
  128. && mutationLocationSet.contains(index)) {
  129. mutation(particle, index);
  130. }
  131. decode(particle, index);
  132. }
  133. } else {
  134. int count = 0;
  135. for (int index = 0; index < dimensions; index++) {
  136. updateVelocity(particle, index, globalBest);
  137. updateGenotype(particle, index);
  138. if (mutationAllowed == 0 && iteration != 0 && Random.nextDouble() < mutationRate) {
  139. count++;
  140. mutation(particle, index);
  141. }
  142. decode(particle, index);
  143. }
  144. bitsFlipped += count;
  145. }
  146. }
  147. if (moreInformation) {
  148. console.println("\t\t\t\t\t\tAvgBitsMutate: " + (bitsFlipped / (double) popsize));
  149. }
  150. if (cancel) {
  151. return null;
  152. }
  153. evaluation(globalBest, swarm);
  154. runList.add(globalBest.fitness);
  155. if (moreInformation) {
  156. console.println("------------------------");
  157. }
  158. }
  159. console.println(" End Value:" + globalBest.fitness);
  160. this.runList = runList;
  161. return globalBest;
  162. }
  163. @Override
  164. protected int getProgressBarMaxCount() {
  165. return rounds * maxGenerations * popsize + 1;
  166. }
  167. @Override
  168. protected String algoInformationToPrint() {
  169. return "PsoAlgo" + " Rounds:" + rounds
  170. + " maxIterations:" + maxGenerations
  171. + " swarmSize:" + popsize
  172. + " dependency:" + dependency
  173. + " mutationInterval:" + mutationInterval
  174. + " maxVelocity: " + maxVelocity
  175. + " deviation: " + deviation
  176. + (useIntervalMutation ?
  177. (" mutateProbabilityInterval:" + mutateProbabilityInterval
  178. + " maxMutationPercent:" + maxMutationPercent) : " mutationRate:" + mutationRate);
  179. }
  180. @Override
  181. protected String plottFileName() {
  182. return "pso-topologie.txt";
  183. }
  184. /**
  185. * @param problemSize maximum index of position in the particle
  186. * @return
  187. */
  188. private List<Particle> initializeParticles(int problemSize) {
  189. List<Particle> swarm = new ArrayList<Particle>();
  190. //Create The Particle
  191. for (int particleNumber = 0; particleNumber < popsize; particleNumber++) {
  192. //Create a Random position
  193. List<Integer> aRandomPosition = new ArrayList<Integer>();
  194. for (int index = 0; index < problemSize; index++) {
  195. aRandomPosition.add(Random.nextIntegerInRange(0, this.getMaximumIndexObjects(index) + 1));
  196. }
  197. swarm.add(new Particle(aRandomPosition));
  198. }
  199. return swarm;
  200. }
  201. /**
  202. * Calculate w, c1, c2
  203. */
  204. private void initDependentParameter() {
  205. w = 1.0 / (dependency - 1 + Math.sqrt(dependency * dependency - 2 * dependency));
  206. c1 = c2 = dependency * w;
  207. }
  208. /**
  209. * Evaluate each particle and update the global Best position;
  210. *
  211. * @param globalBest
  212. * @param swarm
  213. */
  214. private void evaluation(Individual globalBest, List<Particle> swarm) {
  215. for (Particle p : swarm) {
  216. double localEvaluationValue = evaluatePosition(p.xPhenotype);
  217. if (moreInformation) {
  218. console.println("Fitness " + localEvaluationValue);
  219. }
  220. p.checkNewEvaluationValue(localEvaluationValue);
  221. if (localEvaluationValue < globalBest.fitness) {
  222. globalBest.fitness = localEvaluationValue;
  223. globalBest.position = p.localBest.position;
  224. }
  225. }
  226. }
  227. /**
  228. * Eq. (6):v<sub>i,j</sub>(t + 1) = wv<sub>i,j</sub>+c<sub>1</sub>R<sub>1</sub>(P<sub>best,i,j</sub>-x<sub>p,i,j</sub>(t))+c<sub>2</sub>R<sub>2</sub>(g<sub>best,i,j</sub>-x<sub>p,i,j</sub>(t))<br>
  229. *
  230. * @param particle
  231. * @param index
  232. * @param globalBest
  233. */
  234. private void updateVelocity(Particle particle, int index, Individual globalBest) {
  235. double r1 = Random.nextDouble();
  236. double r2 = Random.nextDouble();
  237. double posValue = particle.xPhenotype.get(index);
  238. particle.velocity.set(index, clamp(
  239. w * particle.velocity.get(index) + c1 * r1 * ((particle.localBest.position.get(index))
  240. - posValue) + c2 * r2 * ((globalBest.position.get(index)) - posValue)));
  241. }
  242. /**
  243. * Eq. (7):x<sub>g,i,j</sub>(t + 1) = x<sub>g,i,j</sub>(t) + v<sub>i,j</sub>(t + 1)<br>
  244. *
  245. * @param particle
  246. * @param index
  247. */
  248. private void updateGenotype(Particle particle, int index) {
  249. particle.xGenotype.set(index,
  250. clamp(particle.xGenotype.get(index) + particle.velocity.get(index)));
  251. }
  252. /**
  253. * Eq. (11):<b>if(</b>rand()&lt;r<sub>mu</sub><b>)then</b> x<sub>g,i,j</sub>(t + 1) =
  254. * -x<sub>g,i,j</sub>(t + 1)<br>
  255. *
  256. * @param particle
  257. * @param index
  258. */
  259. private void mutation(Particle particle, int index) {
  260. //if(Random.nextDouble() < limit)
  261. particle.xGenotype.set(index, -particle.xGenotype.get(index));
  262. }
  263. /**
  264. * Eq. (8):x<sub>p,i,j</sub>(t + 1) = <b>(</b>rand() &lt; S(x<sub>g,i,j</sub>(t + 1))<b>) ?</b> 1
  265. * <b>:</b> 0<br>
  266. *
  267. * @param particle
  268. * @param index
  269. */
  270. private void decode(Particle particle, int index) {
  271. double value = clamp(Random.nextGaussian(particle.xGenotype.get(index), deviation));
  272. double alpha = Maths.invLerp(-maxVelocity, +maxVelocity, value);
  273. double result = Maths.lerp(0, this.getMaximumIndexObjects(index), alpha);
  274. particle.xPhenotype.set(index, (int) Math.round(result));
  275. }
  276. /**
  277. * To clamp X<sub>g,j,i</sub> and v<sub>i,j</sub> in Range [-X<sub>g,max</sub>|+X<sub>g,max</sub>]
  278. * with {X<sub>g,max</sub>= 4}
  279. *
  280. * @param value
  281. * @return
  282. */
  283. private double clamp(double value) {
  284. return Math.max(-maxVelocity, Math.min(maxVelocity, value));
  285. }
  286. private TreeSet<Integer> locationsToMutate(int dimensions) {
  287. TreeSet<Integer> mutationLocation = new TreeSet<Integer>(); //sortedSet
  288. int maximumAmountOfMutatedBits = Math.max(1,
  289. (int) Math.round(((double) dimensions) * this.maxMutationPercent));
  290. int randomUniformAmountOfMutatedValues = Random.nextIntegerInRange(1,
  291. maximumAmountOfMutatedBits + 1);
  292. for (int i = 0; i < randomUniformAmountOfMutatedValues; i++) {
  293. boolean success = mutationLocation.add(Random.nextIntegerInRange(0, dimensions));
  294. if (!success) {
  295. i--; //can be add up to some series long loops if maximumAmountOfMutatedBits get closed to problemsize.
  296. }
  297. }
  298. return mutationLocation;
  299. }
  300. /**
  301. * Class to represent a Particle.
  302. */
  303. private class Particle {
  304. /**
  305. * The velocity of a particle.
  306. */
  307. public List<Double> velocity;
  308. /**
  309. * The positions genotype.
  310. */
  311. public List<Double> xGenotype;
  312. /**
  313. * The positions phenotype. Alias the current position.
  314. */
  315. public List<Integer> xPhenotype;
  316. public Individual localBest;
  317. Particle(List<Integer> position) {
  318. this.xPhenotype = position;
  319. //Init velocity, xGenotype with 0.0 values.
  320. this.velocity = position.stream().map(bool -> 0.0).collect(Collectors.toList());
  321. this.xGenotype = position.stream().map(bool -> 0.0).collect(Collectors.toList());
  322. localBest = new Individual();
  323. localBest.fitness = Double.MAX_VALUE;
  324. }
  325. public void checkNewEvaluationValue(double newEvaluationValue) {
  326. if (newEvaluationValue < localBest.fitness) {
  327. localBest.fitness = newEvaluationValue;
  328. localBest.position = xPhenotype.stream().collect(Collectors.toList());
  329. }
  330. }
  331. public String toString() {
  332. return "Particle with xPhenotype(Position), xGenotype, velocity:["
  333. + listToString(xPhenotype) + "],[" + listToString(xGenotype) + "],["
  334. + listToString(velocity) + "]";
  335. }
  336. private <Type> String listToString(List<Type> list) {
  337. return list.stream().map(Object::toString).collect(Collectors.joining(", "));
  338. }
  339. }
  340. }