/* 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.core.EpsilonBoxDominanceArchive;
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.CrowdingComparator;
import org.moeaframework.core.comparator.ParetoDominanceComparator;
import org.moeaframework.core.fitness.CrowdingDistanceFitnessEvaluator;
import org.moeaframework.core.fitness.FitnessBasedArchive;
import org.moeaframework.core.variable.RealVariable;
// NOTE: This implementation is derived from the original manuscripts and the
// JMetal implementation.
/**
* Implementation of OMOPSO, a multi-objective particle swarm optimizer (MOPSO).
* According to [2], OMOPSO is one of the top-performing PSO algorithms.
* <p>
* References:
* <ol>
* <li>Sierra, M. R. and C. A. Coello Coello (2005). Improving PSO-based
* Multi-Objective Optimization using Crowding, Mutation and
* ε-Dominance. Evolutionary Multi-Criterion Optimization,
* pp. 505-519.
* <li>Durillo, J. J., J. Garc�a-Nieto, A. J. Nebro, C. A. Coello Coello,
* F. Luna, and E. Alba (2009). Multi-Objective Particle Swarm
* Optimizers: An Experimental Comparison. Evolutionary Multi-Criterion
* Optimization, pp. 495-509.
* </ol>
*/
public class OMOPSO extends AbstractPSOAlgorithm {
/**
* The uniform mutation operator, whose parameters remain unchanged.
*/
private final Variation uniformMutation;
/**
* The non-uniform mutation operator, whose parameters change during a run.
*/
private final Variation nonUniformMutation;
/**
* Constructs a new OMOPSO instance.
*
* @param problem the problem
* @param swarmSize the number of particles
* @param leaderSize the number of leaders
* @param epsilons the ε-values used in the external archive
* @param mutationProbability the mutation probability for uniform and
* non-uniform mutation
* @param mutationPerturbation the perturbation index for uniform and
* non-uniform mutation
* @param maxIterations the maximum iterations for scaling the non-uniform
* mutation
*/
public OMOPSO(Problem problem, int swarmSize, int leaderSize,
double[] epsilons, double mutationProbability,
double mutationPerturbation, int maxIterations) {
super(problem, swarmSize, leaderSize, new CrowdingComparator(),
new ParetoDominanceComparator(),
new FitnessBasedArchive(new CrowdingDistanceFitnessEvaluator(), leaderSize),
new EpsilonBoxDominanceArchive(epsilons),
null);
this.uniformMutation = new UniformMutation(mutationProbability,
mutationPerturbation);
this.nonUniformMutation = new NonUniformMutation(mutationProbability,
mutationPerturbation, maxIterations);
}
@Override
protected void mutate(int i) {
if (i % 3 == 0) {
particles[i] = nonUniformMutation.evolve(new Solution[] {
particles[i] })[0];
} else if (i % 3 == 1) {
particles[i] = uniformMutation.evolve(new Solution[] {
particles[i] })[0];
}
}
/**
* The non-uniform mutation operator.
*/
private class NonUniformMutation implements Variation {
private final double probability;
private final double perturbation;
private final int maxIterations;
public NonUniformMutation(double probability, double perturbation,
int maxIterations) {
super();
this.probability = probability;
this.perturbation = perturbation;
this.maxIterations = maxIterations;
}
@Override
public int getArity() {
return 1;
}
@Override
public Solution[] evolve(Solution[] parents) {
Solution offspring = parents[0].copy();
for (int i = 0; i < offspring.getNumberOfVariables(); i++) {
if (PRNG.nextDouble() < probability) {
RealVariable variable = (RealVariable)offspring.getVariable(i);
double value = variable.getValue();
if (PRNG.nextBoolean()) {
value += getDelta(variable.getUpperBound() - value);
} else {
value += getDelta(variable.getLowerBound() - value);
}
if (value < variable.getLowerBound()) {
value = variable.getLowerBound();
} else if (value > variable.getUpperBound()) {
value = variable.getUpperBound();
}
variable.setValue(value);
}
}
return new Solution[] { offspring };
}
public double getDelta(double difference) {
int currentIteration = getNumberOfEvaluations() / swarmSize;
double fraction = currentIteration / (double)maxIterations;
return difference * (1.0 - Math.pow(PRNG.nextDouble(),
Math.pow(1.0 - fraction, perturbation)));
}
}
/**
* The uniform mutation operator.
*/
private class UniformMutation implements Variation {
private final double probability;
private final double perturbation;
public UniformMutation(double probability, double perturbation) {
super();
this.probability = probability;
this.perturbation = perturbation;
}
@Override
public int getArity() {
return 1;
}
@Override
public Solution[] evolve(Solution[] parents) {
Solution offspring = parents[0].copy();
for (int i = 0; i < offspring.getNumberOfVariables(); i++) {
if (PRNG.nextDouble() < probability) {
RealVariable variable = (RealVariable)offspring.getVariable(i);
double value = variable.getValue();
value += (PRNG.nextDouble() - 0.5) * perturbation;
if (value < variable.getLowerBound()) {
value = variable.getLowerBound();
} else if (value > variable.getUpperBound()) {
value = variable.getUpperBound();
}
variable.setValue(value);
}
}
return new Solution[] { offspring };
}
}
}