// IBEA.java
//
// Author:
// Juan J. Durillo <durillo@lcc.uma.es>
//
// Copyright (c) 2011 Juan J. Durillo
//
// This program 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.
//
// This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
// This implementation is based on the PISA code:
// http://www.tik.ee.ethz.ch/sop/pisa/selectors/ibea/?page=ibea.php
package org.moeaframework.algorithm.jmetal;
import java.util.ArrayList;
import java.util.List;
import static org.moeaframework.core.FitnessEvaluator.FITNESS_ATTRIBUTE;
import org.moeaframework.core.Population;
import org.moeaframework.core.Problem;
import org.moeaframework.core.Selection;
import org.moeaframework.core.Solution;
import org.moeaframework.core.comparator.DominanceComparator;
import org.moeaframework.core.comparator.FitnessComparator;
import org.moeaframework.core.comparator.ParetoDominanceComparator;
import org.moeaframework.core.operator.RandomInitialization;
import org.moeaframework.core.operator.TournamentSelection;
import org.moeaframework.core.operator.real.PM;
import org.moeaframework.core.operator.real.SBX;
import org.moeaframework.core.operator.real.SBXjMetal;
/**
* This class implements the IBEA algorithm
*/
public class IBEAjMetal {
private Problem problem;
private List<List<Double>> indicatorValues;
private double maxIndicatorValue;
private int populationSize;
private int archiveSize;
private int maxEvaluations;
private Population archive;
private SBXjMetal crossoverOperator;
private PM mutationOperator;
private Selection selectionOperator;
private ParetoDominanceComparator dominanceComparator;
/**
* Constructor
*/
public IBEAjMetal(Problem problem, int populationSize, int archiveSize, int maxEvaluations) {
this.problem = problem;
this.populationSize = populationSize;
this.archiveSize = archiveSize;
this.maxEvaluations = maxEvaluations;
this.crossoverOperator = new SBXjMetal(0.9, 20.0);
this.mutationOperator = new PM(1 / problem.getNumberOfObjectives(), 20.0);
this.selectionOperator = new TournamentSelection(new FitnessComparator(false));
this.dominanceComparator = new ParetoDominanceComparator();
}
/**
* Execute() method
*/
public void run() {
int evaluations;
Population solutionSet;
//Initialize the variables
solutionSet = new Population();
archive = new Population();
evaluations = 0;
//-> Create the initial solutionSet
RandomInitialization init = new RandomInitialization(problem, populationSize);
solutionSet.addAll(init.initialize());
for(Solution sln : solutionSet){
problem.evaluate(sln);
evaluations++;
}
while (evaluations < maxEvaluations) {
if (evaluations % 1000 == 0) {
System.out.println(evaluations);
}
Population union = new Population();
union.addAll(solutionSet);
union.addAll(archive);
calculateFitness(union);
archive = union;
while (archive.size() > populationSize) {
removeWorst(archive);
}
// Create a new offspringPopulation
Solution[] parents = selectionOperator.select(crossoverOperator.getArity(), archive);
//make the crossover
Population offspring = new Population();
Solution[] crossed = crossoverOperator.evolve(parents);
for (Solution crossedChild : crossed) {
offspring.addAll(mutationOperator.evolve(new Solution[]{crossedChild}));
}
for (Solution child : offspring) {
problem.evaluate(child);
evaluations++;
}
solutionSet = offspring;
}
}
/**
* Calculates the hypervolume of that portion of the objective space that is
* dominated by individual a but not by individual b
*/
double calculateHypervolumeIndicator(Solution solutionA, Solution solutionB, int d,
double maximumValues[], double minimumValues[]) {
double a, b, r, max;
double volume;
double rho = 2.0;
r = rho * (maximumValues[d - 1] - minimumValues[d - 1]);
max = minimumValues[d - 1] + r;
a = solutionA.getObjective(d - 1);
if (solutionB == null) {
b = max;
} else {
b = solutionB.getObjective(d - 1);
}
if (d == 1) {
if (a < b) {
volume = (b - a) / r;
} else {
volume = 0;
}
} else {
if (a < b) {
volume
= calculateHypervolumeIndicator(solutionA, null, d - 1, maximumValues, minimumValues) * (b
- a) / r;
volume
+= calculateHypervolumeIndicator(solutionA, solutionB, d - 1, maximumValues, minimumValues)
* (max - b) / r;
} else {
volume
= calculateHypervolumeIndicator(solutionA, solutionB, d - 1, maximumValues, minimumValues)
* (max - a) / r;
}
}
return (volume);
}
/**
* This structure stores the indicator values of each pair of elements
*/
public void computeIndicatorValuesHD(Population solutionSet, double[] maximumValues,
double[] minimumValues) {
List<Solution> A, B;
// Initialize the structures
indicatorValues = new ArrayList<List<Double>>();
maxIndicatorValue = -Double.MAX_VALUE;
for (int j = 0; j < solutionSet.size(); j++) {
A = new ArrayList<>(1);
A.add(solutionSet.get(j));
List<Double> aux = new ArrayList<Double>();
for (Solution solution : solutionSet) {
B = new ArrayList<>(1);
B.add(solution);
int flag = dominanceComparator.compare(A.get(0), B.get(0));
double value;
if (flag == -1) {
value
= -calculateHypervolumeIndicator(A.get(0), B.get(0), problem.getNumberOfObjectives(),
maximumValues, minimumValues);
} else {
value = calculateHypervolumeIndicator(B.get(0), A.get(0), problem.getNumberOfObjectives(),
maximumValues, minimumValues);
}
//Update the max value of the indicator
if (Math.abs(value) > maxIndicatorValue) {
maxIndicatorValue = Math.abs(value);
}
aux.add(value);
}
indicatorValues.add(aux);
}
}
/**
* Calculate the fitness for the individual at position pos
*/
public void fitness(Population solutionSet, int pos) {
double fitness = 0.0;
double kappa = 0.05;
for (int i = 0; i < solutionSet.size(); i++) {
if (i != pos) {
fitness += Math.exp((-1 * indicatorValues.get(i).get(pos) / maxIndicatorValue) / kappa);
}
}
solutionSet.get(pos).setAttribute(FITNESS_ATTRIBUTE, fitness);
}
/**
* Calculate the fitness for the entire population.
*/
public void calculateFitness(Population solutionSet) {
// Obtains the lower and upper bounds of the population
double[] maximumValues = new double[problem.getNumberOfObjectives()];
double[] minimumValues = new double[problem.getNumberOfObjectives()];
for (int i = 0; i < problem.getNumberOfObjectives(); i++) {
maximumValues[i] = -Double.MAX_VALUE;
minimumValues[i] = Double.MAX_VALUE;
}
for (Solution solution : solutionSet) {
for (int obj = 0; obj < problem.getNumberOfObjectives(); obj++) {
double value = solution.getObjective(obj);
if (value > maximumValues[obj]) {
maximumValues[obj] = value;
}
if (value < minimumValues[obj]) {
minimumValues[obj] = value;
}
}
}
computeIndicatorValuesHD(solutionSet, maximumValues, minimumValues);
for (int pos = 0; pos < solutionSet.size(); pos++) {
fitness(solutionSet, pos);
}
}
/**
* Update the fitness before removing an individual
*/
public void removeWorst(Population solutionSet) {
// Find the worst;
double worst = (double) solutionSet.get(0).getAttribute(FITNESS_ATTRIBUTE);
int worstIndex = 0;
double kappa = 0.05;
for (int i = 1; i < solutionSet.size(); i++) {
if ((double) solutionSet.get(i).getAttribute(FITNESS_ATTRIBUTE) > worst) {
worst = (double) solutionSet.get(i).getAttribute(FITNESS_ATTRIBUTE);
worstIndex = i;
}
}
// Update the population
for (int i = 0; i < solutionSet.size(); i++) {
if (i != worstIndex) {
double fitness = (double) solutionSet.get(i).getAttribute(FITNESS_ATTRIBUTE);
fitness -= Math.exp((-indicatorValues.get(worstIndex).get(i) / maxIndicatorValue) / kappa);
solutionSet.get(i).setAttribute(FITNESS_ATTRIBUTE, fitness);
}
}
// remove worst from the indicatorValues list
indicatorValues.remove(worstIndex);
for (List<Double> anIndicatorValues_ : indicatorValues) {
anIndicatorValues_.remove(worstIndex);
}
solutionSet.remove(worstIndex);
}
public Population getArchive() {
return archive;
}
}