// SMSEMOA.java // // Author: // Simon Wessing // // Copyright (c) 2011 Simon Wessing // // 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/>. package jmetal.metaheuristics.smsemoa; import jmetal.core.*; import jmetal.qualityIndicator.Hypervolume; import jmetal.qualityIndicator.QualityIndicator; import jmetal.qualityIndicator.util.MetricsUtil; import jmetal.util.JMException; import jmetal.util.Ranking; import jmetal.util.comparators.CrowdingDistanceComparator; import java.util.Collections; import java.util.LinkedList; /** * This class implements the SMS-EMOA algorithm, as described in * * Michael Emmerich, Nicola Beume, and Boris Naujoks. * An EMO algorithm using the hypervolume measure as selection criterion. * In C. A. Coello Coello et al., Eds., Proc. Evolutionary Multi-Criterion Optimization, * 3rd Int'l Conf. (EMO 2005), LNCS 3410, pp. 62-76. Springer, Berlin, 2005. * * and * * Boris Naujoks, Nicola Beume, and Michael Emmerich. * Multi-objective optimisation using S-metric selection: Application to * three-dimensional solution spaces. In B. McKay et al., Eds., Proc. of the 2005 * Congress on Evolutionary Computation (CEC 2005), Edinburgh, Band 2, pp. 1282-1289. * IEEE Press, Piscataway NJ, 2005. */ public class SMSEMOA extends Algorithm { /** * stores the problem to solve */ private MetricsUtil utils_; private Hypervolume hv_; /** * Constructor * @param problem Problem to solve */ public SMSEMOA(Problem problem) { super(problem) ; this.utils_ = new jmetal.qualityIndicator.util.MetricsUtil(); this.hv_ = new Hypervolume(); } // SMSEMOA /** * Runs the SMS-EMOA algorithm. * @return a <code>SolutionSet</code> that is a set of non dominated solutions * as a result of the algorithm execution * @throws JMException */ public SolutionSet execute() throws JMException, ClassNotFoundException { int populationSize; int maxEvaluations; int evaluations; double offset = 100.0; QualityIndicator indicators; // QualityIndicator object int requiredEvaluations; // Use in the example of use of the indicators object (see below) SolutionSet population; SolutionSet offspringPopulation; SolutionSet union; Operator mutationOperator; Operator crossoverOperator; Operator selectionOperator; //Read the parameters populationSize = ((Integer) getInputParameter("populationSize")).intValue(); maxEvaluations = ((Integer) getInputParameter("maxEvaluations")).intValue(); indicators = (QualityIndicator) getInputParameter("indicators"); offset = (Double) getInputParameter("offset"); //Initialize the variables population = new SolutionSet(populationSize); evaluations = 0; requiredEvaluations = 0; //Read the operators mutationOperator = operators_.get("mutation"); crossoverOperator = operators_.get("crossover"); selectionOperator = operators_.get("selection"); // Create the initial solutionSet Solution newSolution; for (int i = 0; i < populationSize; i++) { newSolution = new Solution(problem_); problem_.evaluate(newSolution); problem_.evaluateConstraints(newSolution); evaluations++; population.add(newSolution); } //for // Generations ... while (evaluations < maxEvaluations) { // select parents offspringPopulation = new SolutionSet(populationSize); LinkedList<Solution> selectedParents = new LinkedList<Solution>(); Solution[] parents = new Solution[0]; while (selectedParents.size() < 2) { Object selected = selectionOperator.execute(population); try { Solution parent = (Solution) selected; selectedParents.add(parent); } catch (ClassCastException e) { parents = (Solution[]) selected; Collections.addAll(selectedParents, parents); } } parents = selectedParents.toArray(parents); // crossover Solution[] offSpring = (Solution[]) crossoverOperator.execute(parents); // mutation mutationOperator.execute(offSpring[0]); // evaluation problem_.evaluate(offSpring[0]); problem_.evaluateConstraints(offSpring[0]); // insert child into the offspring population offspringPopulation.add(offSpring[0]); evaluations++; // Create the solutionSet union of solutionSet and offSpring union = ((SolutionSet) population).union(offspringPopulation); // Ranking the union (non-dominated sorting) Ranking ranking = new Ranking(union); // ensure crowding distance values are up to date // (may be important for parent selection) for (int j = 0; j < population.size(); j++) { population.get(j).setCrowdingDistance(0.0); } SolutionSet lastFront = ranking.getSubfront(ranking.getNumberOfSubfronts() - 1); if (lastFront.size() > 1) { double[][] frontValues = lastFront.writeObjectivesToMatrix(); int numberOfObjectives = problem_.getNumberOfObjectives(); // STEP 1. Obtain the maximum and minimum values of the Pareto front double[] maximumValues = utils_.getMaximumValues(union.writeObjectivesToMatrix(), numberOfObjectives); double[] minimumValues = utils_.getMinimumValues(union.writeObjectivesToMatrix(), numberOfObjectives); // STEP 2. Get the normalized front double[][] normalizedFront = utils_.getNormalizedFront(frontValues, maximumValues, minimumValues); // compute offsets for reference point in normalized space double[] offsets = new double[maximumValues.length]; for (int i = 0; i < maximumValues.length; i++) { offsets[i] = offset / (maximumValues[i] - minimumValues[i]); } // STEP 3. Inverse the pareto front. This is needed because the original //metric by Zitzler is for maximization problems double[][] invertedFront = utils_.invertedFront(normalizedFront); // shift away from origin, so that boundary points also get a contribution > 0 for (double[] point : invertedFront) { for (int i = 0; i < point.length; i++) { point[i] += offsets[i]; } } // calculate contributions and sort double[] contributions = hvContributions(invertedFront); for (int i = 0; i < contributions.length; i++) { // contribution values are used analogously to crowding distance lastFront.get(i).setCrowdingDistance(contributions[i]); } lastFront.sort(new CrowdingDistanceComparator()); } // all but the worst are carried over to the survivor population SolutionSet front = null; population.clear(); for (int i = 0; i < ranking.getNumberOfSubfronts() - 1; i++) { front = ranking.getSubfront(i); for (int j = 0; j < front.size(); j++) { population.add(front.get(j)); } } for (int i = 0; i < lastFront.size() - 1; i++) { population.add(lastFront.get(i)); } // This piece of code shows how to use the indicator object into the code // of SMS-EMOA. In particular, it finds the number of evaluations required // by the algorithm to obtain a Pareto front with a hypervolume higher // than the hypervolume of the true Pareto front. if (indicators != null && requiredEvaluations == 0) { double HV = indicators.getHypervolume(population); if (HV >= (0.98 * indicators.getTrueParetoFrontHypervolume())) { requiredEvaluations = evaluations; } // if } // if } // while // Return as output parameter the required evaluations setOutputParameter("evaluations", requiredEvaluations); // Return the first non-dominated front Ranking ranking = new Ranking(population); ranking.getSubfront(0).printFeasibleFUN("FUN") ; return ranking.getSubfront(0); } // execute /** * Calculates how much hypervolume each point dominates exclusively. The points * have to be transformed beforehand, to accommodate the assumptions of Zitzler's * hypervolume code. * @param front transformed objective values * @return HV contributions */ private double[] hvContributions(double[][] front) { int numberOfObjectives = problem_.getNumberOfObjectives(); double[] contributions = new double[front.length]; double[][] frontSubset = new double[front.length - 1][front[0].length]; LinkedList<double[]> frontCopy = new LinkedList<double[]>(); Collections.addAll(frontCopy, front); double[][] totalFront = frontCopy.toArray(frontSubset); double totalVolume = hv_.calculateHypervolume(totalFront, totalFront.length, numberOfObjectives); for (int i = 0; i < front.length; i++) { double[] evaluatedPoint = frontCopy.remove(i); frontSubset = frontCopy.toArray(frontSubset); // STEP4. The hypervolume (control is passed to java version of Zitzler code) double hv = hv_.calculateHypervolume(frontSubset, frontSubset.length, numberOfObjectives); double contribution = totalVolume - hv; contributions[i] = contribution; // put point back frontCopy.add(i, evaluatedPoint); } return contributions; } } // SMSEMOA