/* 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.core; import java.util.Iterator; import org.moeaframework.core.comparator.DominanceComparator; import org.moeaframework.core.comparator.ParetoDominanceComparator; /** * A population that maintains the property of pair-wise non-dominance between * all solutions. When the {@code add} method is invoked with a new solution, * all solutions currently in the population that are dominated by the new * solution are removed. If the new solution is dominated by any member of the * population, the new solution is not added. */ public class NondominatedPopulation extends Population { /** * The dominance comparator used by this non-dominated population. */ protected final DominanceComparator comparator; /** * If {@code true}, allow duplicate solutions in this non-dominated * population. Duplicate solutions are those whose Euclidean distance * is smaller than {@code Settings.EPSILON}. */ protected final boolean allowDuplicates; /** * Constructs an empty non-dominated population using the Pareto dominance * relation. */ public NondominatedPopulation() { this(new ParetoDominanceComparator()); } /** * Constructs an empty non-dominated population using the specified * dominance relation. * * @param comparator the dominance relation used by this non-dominated * population */ public NondominatedPopulation(DominanceComparator comparator) { this(comparator, false); } /** * Constructs an empty non-dominated population using the specified * dominance relation. * * @param comparator the dominance relation used by this non-dominated * population * @param allowDuplicates allow duplicate solutions into the non-dominated * population */ public NondominatedPopulation(DominanceComparator comparator, boolean allowDuplicates) { super(); this.comparator = comparator; this.allowDuplicates = allowDuplicates; } /** * Constructs a non-dominated population using the Pareto dominance relation * and initialized with the specified solutions. * * @param iterable the solutions used to initialize this non-dominated * population */ public NondominatedPopulation(Iterable<? extends Solution> iterable) { this(); addAll(iterable); } /** * Constructs a non-dominated population using the specified dominance * comparator and initialized with the specified solutions. * * @param comparator the dominance relation used by this non-dominated * population * @param iterable the solutions used to initialize this non-dominated * population */ public NondominatedPopulation(DominanceComparator comparator, Iterable<? extends Solution> iterable) { this(comparator); addAll(iterable); } /** * If {@code newSolution} is dominates any solution or is non-dominated with * all solutions in this population, the dominated solutions are removed and * {@code newSolution} is added to this population. Otherwise, * {@code newSolution} is dominated and is not added to this population. */ @Override public boolean add(Solution newSolution) { Iterator<Solution> iterator = iterator(); while (iterator.hasNext()) { Solution oldSolution = iterator.next(); int flag = comparator.compare(newSolution, oldSolution); if (flag < 0) { iterator.remove(); } else if (flag > 0) { return false; } else if (!allowDuplicates && distance(newSolution, oldSolution) < Settings.EPS) { return false; } } return super.add(newSolution); } /** * Replace the solution at the given index with the new solution, but only * if the new solution is non-dominated. To maintain non-dominance within * this population, any solutions dominated by the new solution will also * be replaced. */ @Override public void replace(int index, Solution newSolution) { Iterator<Solution> iterator = iterator(); while (iterator.hasNext()) { Solution oldSolution = iterator.next(); int flag = comparator.compare(newSolution, oldSolution); if (flag < 0) { iterator.remove(); } else if (flag > 0) { return; } else if (!allowDuplicates && distance(newSolution, oldSolution) < Settings.EPS) { return; } } super.replace(index, newSolution); } /** * Adds the specified solution to the population, bypassing the * non-domination check. This method should only be used when a * non-domination check has been performed elsewhere, such as in a subclass. * <p> * <b>This method should only be used internally, and should never be made * public by any subclasses.</b> * * @param newSolution the solution to be added * @return true if the population was modified as a result of this operation */ protected boolean forceAddWithoutCheck(Solution newSolution) { return super.add(newSolution); } /** * Returns the Euclidean distance between two solutions in objective space. * * @param s1 the first solution * @param s2 the second solution * @return the distance between the two solutions in objective space */ protected double distance(Solution s1, Solution s2) { double distance = 0.0; for (int i = 0; i < s1.getNumberOfObjectives(); i++) { distance += Math.pow(s1.getObjective(i) - s2.getObjective(i), 2.0); } return Math.sqrt(distance); } /** * Returns the dominance comparator used by this non-dominated population. * * @return the dominance comparator used by this non-dominated population */ public DominanceComparator getComparator() { return comparator; } }