/* 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 java.io.NotSerializableException; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.moeaframework.algorithm.AbstractAlgorithm; import org.moeaframework.algorithm.AlgorithmInitializationException; 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); } } @Override public Serializable getState() throws NotSerializableException { if (!isInitialized()) { throw new AlgorithmInitializationException(this, "algorithm not initialized"); } List<Solution> particlesList = Arrays.asList(particles); List<Solution> localBestParticlesList = Arrays.asList(localBestParticles); List<Solution> leadersList = new ArrayList<Solution>(); List<Solution> archiveList = new ArrayList<Solution>(); double[][] velocitiesClone = new double[velocities.length][]; for (Solution solution : leaders) { leadersList.add(solution); } if (archive != null) { for (Solution solution : archive) { archiveList.add(solution); } } for (int i = 0; i < velocities.length; i++) { velocitiesClone[i] = velocities[i].clone(); } return new PSOAlgorithmState(getNumberOfEvaluations(), particlesList, localBestParticlesList, leadersList, archiveList, velocitiesClone); } @Override public void setState(Object objState) throws NotSerializableException { super.initialize(); PSOAlgorithmState state = (PSOAlgorithmState)objState; numberOfEvaluations = state.getNumberOfEvaluations(); if (state.getParticles().size() != swarmSize) { throw new NotSerializableException( "swarmSize does not match serialized state"); } for (int i = 0; i < swarmSize; i++) { particles[i] = state.getParticles().get(i); } for (int i = 0; i < swarmSize; i++) { localBestParticles[i] = state.getLocalBestParticles().get(i); } leaders.addAll(state.getLeaders()); leaders.update(); if (archive != null) { archive.addAll(state.getArchive()); } for (int i = 0; i < swarmSize; i++) { for (int j = 0; j < problem.getNumberOfVariables(); j++) { velocities[i][j] = state.getVelocities()[i][j]; } } } /** * Proxy for serializing and deserializing the state of an * {@code AbstractPSOAlgorithm}. This proxy supports saving * the {@code numberOfEvaluations}, {@code population} and {@code archive}. */ private static class PSOAlgorithmState implements Serializable { private static final long serialVersionUID = -1895823731827106938L; /** * The number of objective function evaluations. */ private final int numberOfEvaluations; /** * The particles stored in a serializable list. */ private final List<Solution> particles; /** * The local best particles stored in a serializable list. */ private final List<Solution> localBestParticles; /** * The leaders stored in a serializable list. */ private final List<Solution> leaders; /** * The archive stored in a serializable list. */ private final List<Solution> archive; /** * The velocities. */ private final double[][] velocities; /** * Constructs a proxy to serialize and deserialize the state of an * {@code AbstractPSOAlgorithm}. * * @param numberOfEvaluations the number of objective function * evaluations * @param population the population stored in a serializable list * @param archive the archive stored in a serializable list */ public PSOAlgorithmState(int numberOfEvaluations, List<Solution> particles, List<Solution> localBestParticles, List<Solution> leaders, List<Solution> archive, double[][] velocities) { super(); this.numberOfEvaluations = numberOfEvaluations; this.particles = particles; this.localBestParticles = localBestParticles; this.leaders = leaders; this.archive = archive; this.velocities = velocities; } /** * Returns the number of objective function evaluations. * * @return the number of objective function evaluations */ public int getNumberOfEvaluations() { return numberOfEvaluations; } /** * Returns the particles stored in a serializable list. * * @return the particles stored in a serializable list */ public List<Solution> getParticles() { return particles; } /** * Returns the local best particles stored in a serializable list. * * @return the local best particles stored in a serializable list */ public List<Solution> getLocalBestParticles() { return localBestParticles; } /** * Returns the leaders stored in a serializable list. * * @return the leaders stored in a serializable list */ public List<Solution> getLeaders() { return leaders; } /** * Returns the velocities. * * @return the velocities */ public double[][] getVelocities() { return velocities; } /** * Returns the archive stored in a serializable list. * * @return the archive stored in a serializable list */ public List<Solution> getArchive() { return archive; } } }