/** * */ package vroom.trsp.optimization.biobj; import java.util.Iterator; import java.util.SortedSet; import vroom.trsp.datamodel.TRSPSolution; import vroom.trsp.optimization.biobj.ParetoFront.ParetoSolution; /** * <code>HierarchicalParetoSelector</code> selects the best solution according to the second objective allowing a fixed * degradation with respect to the best solution of the first objective. * <p> * Creation date: Dec 13, 2011 - 1:55:03 PM * * @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a * href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a * href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a> * @version 1.0 */ public class HierarchicalParetoSelector implements IParetoSelector { private final double mAllowedDegradation; /** * Returns the allowed degradation on the first objective. * <p> * For instance, return a value of {@code 1} if no degradation is allowed, {@code 1.1} if a degradation of up to 1% * is allowed, and {@link Double#POSITIVE_INFINITY} if the first objective should be ignored * </p> * * @return the allowed degradation on the first objective */ public double getAllowedDegradation() { return mAllowedDegradation; } /** * Creates a new <code>HierarchicalParetoSelector</code> * * @param allowedDegradation * the allowed degradation on the first objective, for instance, a value of {@code 1} if no degradation * is allowed, {@code 1.1} if a degradation of up to 1% is allowed, and {@link Double#POSITIVE_INFINITY} * if the first objective should be ignored */ public HierarchicalParetoSelector(double allowedDegradation) { mAllowedDegradation = allowedDegradation; } /* * (non-Javadoc) * @see vroom.trsp.optimization.biobj.IParetoSelector#selectSolution(vroom.trsp.optimization.biobj.ParetoFront) */ @Override public ParetoSolution selectParetoSolution(ParetoFront pareto) { double allowedDeg = mAllowedDegradation; SortedSet<ParetoSolution> solutions; if (allowedDeg == Double.POSITIVE_INFINITY) { // We use the solutions sorted according to the first objective // Thus the first feasible will be the best according to the second objective solutions = pareto.getSolutionsFirstObj(); // We will select the first feasible solution allowedDeg = 1; } else { // We use the solutions sorted according to the second objective // Thus the first feasible will be the best according to the first objective solutions = pareto.getSolutionsSecondObj(); } // Iterate over the solution set looking for the first feasible solution or the best solution with the minimum // number of unserved requests ParetoSolution selectedFeasSolution = null; ParetoSolution selectedSolution = null; double threshold = Double.NaN; for (ParetoSolution sol : solutions) { if (sol.getSolution().getUnservedCount() == 0) { if (allowedDeg == 1) // We found the first (best) feasible solution, return it return sol; if (selectedFeasSolution == null) { // We found the best feasible solution selectedFeasSolution = sol; selectedSolution = sol; // Evaluate the threshold on the first objective threshold = sol.getFirstObjValue() * allowedDeg; } else if (sol.getFirstObjValue() < threshold) { // We found a feasible solution that is acceptable regarding the threshold // temporarily set it as the accepted solution selectedFeasSolution = sol; selectedSolution = sol; } } else if (selectedSolution == null || sol.getSolution().getUnservedCount() < selectedSolution.getSolution() .getUnservedCount()) { // We found an infeasible solution with the lowest number of unserved requests so far selectedSolution = sol; } if (selectedFeasSolution != null && sol.getFirstObjValue() > threshold) // We have a feasible solution and the current solution is above the threshold // Break the loop and return the selected feasible solution return selectedFeasSolution; } // Return the second choice (which is either the selectedFeasSolution or the best solution with the minimum // number of unserved requests) return selectedSolution; } @Override public TRSPSolution selectSolution(ParetoFront pareto) { ParetoSolution s = selectParetoSolution(pareto); return s != null ? s.getSolution() : null; } /** * Return the first feasible solution in {@code subset}, or the first feasible solution of {@code set}, or finally * the solution with the greatest number of served requests * * @param subset * a subset of solutions * @param set * the complete set of solutions * @return the first feasible solution if any, or the first solution of {@code set} * @deprecated this implementation has been replaced by {@link #selectParetoSolution(ParetoFront)} */ @Deprecated protected ParetoSolution firstFeasibleSolution(SortedSet<ParetoSolution> subset, SortedSet<ParetoSolution> set) { // FIXME check if the solution selection is correct, in particular ensure that we dont miss feasible solutions // A way to do that would be to restrict first to the set of feasible solutions, and if it is empty then to // consider the whole set Iterator<ParetoSolution> it = subset.iterator(); if (!it.hasNext()) return null; ParetoSolution s = null; ParetoSolution best = null; while (it.hasNext()) { s = it.next(); if (s.getSolution().getUnservedCount() == 0) return s; else if (best == null || s.getSolution().getUnservedCount() < best.getSolution().getUnservedCount()) best = s; } return best; } }