/* 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; import java.io.NotSerializableException; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.moeaframework.algorithm.ReferenceVectorGuidedPopulation.ReferenceVectorGuidedPopulationState; import org.moeaframework.core.FrameworkException; import org.moeaframework.core.Initialization; import org.moeaframework.core.PRNG; import org.moeaframework.core.Population; import org.moeaframework.core.Problem; import org.moeaframework.core.Solution; import org.moeaframework.core.Variation; /** * Implementation of the Reference Vector Guided Evolutionary Algorithm (RVEA). * This version does not include the reference vector regeneration method * proposed by the authors. * <p> * RVEA is similar in concept to NSGA-III, but replaces NSGA-III's * dominance-based selection with an angle-penalized distance function. * Additionally, whereas NSGA-III renormalizes the objectives every iteration, * RVEA periodically scales the reference vectors, potentially reducing * algorithm overhead. * <p> * References: * <ol> * <li>R. Cheng, Y. Jin, M. Olhofer, and B. Sendhoff. "A Reference Vector * Guided Evolutionary Algorithm for Many-objective Optimization." * IEEE Transactions on Evolutionary Computation, Issue 99, 2016. * </ol> */ public class RVEA extends AbstractEvolutionaryAlgorithm { /** * The variation operator. */ private final Variation variation; /** * The current generation; */ private int generation; /** * The maximum number of generations for the angle-penalized distance to * transition between convergence and diversity. */ private int maxGeneration = 1000; /** * The frequency, in generations, that the reference vectors are normalized. */ private int adaptFrequency = maxGeneration / 10; /** * Constructs a new instance of the RVEA algorithm. * * @param problem the problem being solved * @param population the population used to store solutions * @param variation the variation operator * @param initialization the initialization method * @param maxGeneration the maximum number of generations for the * angle-penalized distance to transition between convergence and * diversity * @param adaptFrequency the frequency, in generations, that the reference * vectors are normalized. */ public RVEA(Problem problem, ReferenceVectorGuidedPopulation population, Variation variation, Initialization initialization, int maxGeneration, int adaptFrequency) { super(problem, population, null, initialization); this.variation = variation; // catch potential errors if (variation.getArity() != 2) { throw new FrameworkException( "RVEA only supports operators requiring 2 parents"); } } @Override protected void iterate() { ReferenceVectorGuidedPopulation population = getPopulation(); Population offspring = new Population(); int populationSize = population.size(); // update the scaling factor for computing the angle-penalized distance population.setScalingFactor(Math.min(generation / (double)maxGeneration, 1.0)); // create a random permutation of the population indices List<Integer> indices = new ArrayList<Integer>(); for (int i = 0; i < populationSize; i++) { indices.add(i); } PRNG.shuffle(indices); // add an extra entry so the number of indices is even if (indices.size() % 2 == 1) { indices.add(indices.get(0)); } // generate the offspring for (int i = 0; i < indices.size(); i += 2) { Solution[] parents = new Solution[] { population.get(indices.get(i)), population.get(indices.get(i+1)) }; Solution[] children = variation.evolve(parents); offspring.addAll(children); } evaluateAll(offspring); // select the survivors population.addAll(offspring); population.truncate(); // periodically normalize the reference vectors if ((generation > 0) && (generation % adaptFrequency == 0)) { population.adapt(); } generation++; } @Override public ReferenceVectorGuidedPopulation getPopulation() { return (ReferenceVectorGuidedPopulation)super.getPopulation(); } @Override public Serializable getState() throws NotSerializableException { if (!isInitialized()) { throw new AlgorithmInitializationException(this, "algorithm not initialized"); } return new RVEAState(getNumberOfEvaluations(), generation, getPopulation().getState()); } @Override public void setState(Object objState) throws NotSerializableException { RVEAState state = (RVEAState)objState; initialized = true; numberOfEvaluations = state.getNumberOfEvaluations(); generation = state.getGeneration(); getPopulation().setState(state.getPopulationState()); } /** * Proxy for serializing and deserializing the state of an * {@code RVEA} instance. This proxy supports saving * the {@code numberOfEvaluations}, {@code generation}, {@code population} * and {@code archive}. */ private static class RVEAState implements Serializable { private static final long serialVersionUID = 5341464818762163296L; /** * The number of objective function evaluations. */ private final int numberOfEvaluations; /** * The current generation. */ private final int generation; /** * The population stored in a serializable list. */ private final ReferenceVectorGuidedPopulationState populationState; /** * Constructs a proxy to serialize and deserialize the state of an * {@code RVEA} instance. * * @param numberOfEvaluations the number of objective function * evaluations * @param generation the current generation * @param population the population stored in a serializable object */ public RVEAState(int numberOfEvaluations, int generation, ReferenceVectorGuidedPopulationState populationState) { super(); this.numberOfEvaluations = numberOfEvaluations; this.generation = generation; this.populationState = populationState; } /** * Returns the number of objective function evaluations. * * @return the number of objective function evaluations */ public int getNumberOfEvaluations() { return numberOfEvaluations; } /** * Returns the current generation. * * @return the current generation */ public int getGeneration() { return generation; } /** * Returns the population stored in a serializable object. * * @return the population stored in a serializable object */ public ReferenceVectorGuidedPopulationState getPopulationState() { return populationState; } } }