/* Copyright 2009-2016 David Hadka * * This file is part of the MOEA Framework. * * The MOEA Framework is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * The MOEA Framework is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the MOEA Framework. If not, see <http://www.gnu.org/licenses/>. */ package org.moeaframework.algorithm.pso; import org.moeaframework.algorithm.AbstractAlgorithm; import org.moeaframework.core.NondominatedPopulation; import org.moeaframework.core.PRNG; import org.moeaframework.core.Problem; import org.moeaframework.core.Solution; import org.moeaframework.core.Variation; import org.moeaframework.core.comparator.DominanceComparator; import org.moeaframework.core.fitness.FitnessBasedArchive; import org.moeaframework.core.operator.RandomInitialization; import org.moeaframework.core.variable.EncodingUtils; import org.moeaframework.core.variable.RealVariable; /** * Abstract multi-objective particle swarm optimizer (MOPSO). */ public abstract class AbstractPSOAlgorithm extends AbstractAlgorithm { /** * The original implementation of OMOPSO in JMetal returns the leaders * instead of the epsilon-dominance archive as described in the literature. * This results in a small performance difference that is detected by our * unit tests. To enable unit testing to compare the two implementations, * this flag forces OMOPSO to behave like the JMetal implementation and * only return the leaders. */ static boolean TESTING_MODE = false; /** * The number of particles. */ protected int swarmSize; /** * The number of leaders. */ protected int leaderSize; /** * The particles. */ protected Solution[] particles; /** * The local best particles. */ protected Solution[] localBestParticles; /** * The leaders. */ protected FitnessBasedArchive leaders; /** * The archive of non-dominated solutions; or {@code null} of no external * archive is sued. */ protected NondominatedPopulation archive; /** * The speed / velocity of each particle. */ protected double[][] velocities; /** * Comparator for selecting leaders. */ protected DominanceComparator leaderComparator; /** * Comparator for updating the local best particles. */ protected DominanceComparator dominanceComparator; /** * Mutation operator, or {@code null} if no mutation is defined. */ protected Variation mutation; /** * Constructs a new abstract PSO algorithm. * * @param problem the problem * @param swarmSize the number of particles * @param leaderSize the number of leaders * @param leaderComparator comparator for selecting leaders * @param dominanceComparator comparator for updating the local best * particles * @param leaders non-dominated population for storing the leaders * @param archive non-dominated population for storing the external archive; * or {@code null} if no external archive is defined * @param mutation mutation operator, or {@code null} if no mutation is * defined */ public AbstractPSOAlgorithm(Problem problem, int swarmSize, int leaderSize, DominanceComparator leaderComparator, DominanceComparator dominanceComparator, FitnessBasedArchive leaders, NondominatedPopulation archive, Variation mutation) { super(problem); this.swarmSize = swarmSize; this.leaderSize = leaderSize; this.leaderComparator = leaderComparator; this.dominanceComparator = dominanceComparator; this.leaders = leaders; this.archive = archive; this.mutation = mutation; particles = new Solution[swarmSize]; localBestParticles = new Solution[swarmSize]; velocities = new double[swarmSize][problem.getNumberOfVariables()]; } /** * Update the speeds of all particles. */ protected void updateVelocities() { for (int i = 0; i < swarmSize; i++) { updateVelocity(i); } } /** * Update the speed of an individual particle. * * @param i the index of the particle */ protected void updateVelocity(int i) { Solution particle = particles[i]; Solution localBestParticle = localBestParticles[i]; Solution leader = selectLeader(); double r1 = PRNG.nextDouble(); double r2 = PRNG.nextDouble(); double C1 = PRNG.nextDouble(1.5, 2.0); double C2 = PRNG.nextDouble(1.5, 2.0); double W = PRNG.nextDouble(0.1, 0.5); for (int j = 0; j < problem.getNumberOfVariables(); j++) { double particleValue = EncodingUtils.getReal(particle.getVariable(j)); double localBestValue = EncodingUtils.getReal(localBestParticle.getVariable(j)); double leaderValue = EncodingUtils.getReal(leader.getVariable(j)); velocities[i][j] = W * velocities[i][j] + C1*r1*(localBestValue - particleValue) + C2*r2*(leaderValue - particleValue); } } /** * Update the positions of all particles. */ protected void updatePositions() { for (int i = 0; i < swarmSize; i++) { updatePosition(i); } } /** * Update the position of an individual particle. * * @param i the index of the particle */ protected void updatePosition(int i) { Solution parent = particles[i]; Solution offspring = parent.copy(); for (int j = 0; j < problem.getNumberOfVariables(); j++) { RealVariable variable = (RealVariable)offspring.getVariable(j); double value = variable.getValue() + velocities[i][j]; if (value < variable.getLowerBound()) { value = variable.getLowerBound(); velocities[i][j] *= -1; } else if (value > variable.getUpperBound()) { value = variable.getUpperBound(); velocities[i][j] *= -1; } variable.setValue(value); } particles[i] = offspring; } /** * Randomly select a leader. * * @return the selected leader */ protected Solution selectLeader() { Solution leader1 = leaders.get(PRNG.nextInt(leaders.size())); Solution leader2 = leaders.get(PRNG.nextInt(leaders.size())); int flag = leaderComparator.compare(leader1, leader2); if (flag < 0) { return leader1; } else if (flag > 0) { return leader2; } else if (PRNG.nextBoolean()) { return leader1; } else { return leader2; } } /** * Updates the local best particles. */ protected void updateLocalBest() { for (int i = 0; i < swarmSize; i++) { int flag = dominanceComparator.compare(particles[i], localBestParticles[i]); if (flag <= 0) { localBestParticles[i] = particles[i]; } } } /** * Applies the mutation operator to all particles. */ protected void mutate() { for (int i = 0; i < swarmSize; i++) { mutate(i); } } /** * Applies the mutation operator to an individual particle. * * @param i the index of the particle */ protected void mutate(int i) { if (mutation != null) { particles[i] = mutation.evolve(new Solution[] { particles[i] })[0]; } } @Override public NondominatedPopulation getResult() { if (archive == null || TESTING_MODE) { return new NondominatedPopulation(leaders); } else { return new NondominatedPopulation(archive); } } @Override protected void initialize() { super.initialize(); Solution[] initialParticles = new RandomInitialization(problem, swarmSize).initialize(); evaluateAll(initialParticles); for (int i = 0; i < swarmSize; i++) { particles[i] = initialParticles[i]; localBestParticles[i] = initialParticles[i]; } leaders.addAll(initialParticles); leaders.update(); if (archive != null) { archive.addAll(initialParticles); } } @Override protected void iterate() { updateVelocities(); updatePositions(); mutate(); evaluateAll(particles); updateLocalBest(); leaders.addAll(particles); leaders.update(); if (archive != null) { archive.addAll(particles); } } }