package eu.scape_project.planning.criteria.bean; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Set; import eu.scape_project.planning.model.kbrowser.VPlanLeaf; /** * Calculator for dominates measures and sets of measures. * * A set of measures is dominated if changing the value of a measure could * potentially change the ranking of alternatives. * * For all leaves, the lower and upper boundary of possible values weighted * according to the leaf is calculated. If these boundaries overlap, a change in * the values could potentially change the ranking of alternatives. * * @see "Improving decision support for software component selection throughsystematic cross-referencing and analysis of multiple decision criteria, Christoph Becker et al." */ public class RankChangingDominatedSets extends DominatedSets { /** * Creates a new DominatedSetCalculator. * * @param selectedPlans * PlanInfos to use * @param selectedLeaves * leaves to use */ public RankChangingDominatedSets(final List<PlanInfo> selectedPlans, final List<VPlanLeaf> selectedLeaves) { super(selectedPlans, selectedLeaves); } /** * Returns the result ranges for each alternative of the plan. The ranges * describe the potential ranges of values for the provided measures. * * @param planInfo * the plan * @param measureUris * measures to take into account * @return a list of result ranges */ public List<AlternativeResultRange> getDominatedSetBounds(final PlanInfo planInfo, Collection<String> measureUris) { List<AlternativeResultRange> resultRanges = createAlternativeResultRanges(planInfo); Map<PlanInfo, Set<VPlanLeaf>> leavesOfMeasureUris = getLeavesOfMeasureUris(measureUris); Set<VPlanLeaf> leavesOfPlan = leavesOfMeasureUris.get(planInfo); updateBounds(leavesOfPlan, resultRanges); return resultRanges; } /** * Checks if the provided leaves could influence the ranking of * alternatives. * * @param planInfo * plan info * @param leafSet * leaves to check * @return true if the leaves could change the ranking, false otherwise */ @Override protected boolean isLeafSetDominated(final PlanInfo planInfo, final Set<VPlanLeaf> leafSet) { for (VPlanLeaf leaf : leafSet) { if (leaf.hasKOPotential()) { return false; } } List<AlternativeResultRange> resultRanges = createAlternativeResultRanges(planInfo); updateBounds(leafSet, resultRanges); return !rangesOverlapping(resultRanges); } /** * Creates a list of result ranges from the planinfo. * * @param planInfo * the planinfo * @return a list of result ranges */ private List<AlternativeResultRange> createAlternativeResultRanges(final PlanInfo planInfo) { Map<String, Double> results = planInfo.getOverallResults().getResults(); List<AlternativeResultRange> resultRanges = new ArrayList<AlternativeResultRange>(results.size()); for (Map.Entry<String, Double> result : results.entrySet()) { resultRanges.add(new AlternativeResultRange(result.getKey(), result.getValue())); } return resultRanges; } /** * Updates the lower and upper bounds for each alternative to reflect * potential minimum and maximum weighted results in case the provided * leaves are set to their potential minimum and maximum values * respectively. * * @param leafSet * leaves for the plan to consider * @param resultRanges * the result ranges to update */ private void updateBounds(final Set<VPlanLeaf> leafSet, List<AlternativeResultRange> resultRanges) { for (VPlanLeaf leaf : leafSet) { double leafMinimum = leaf.getPotentialMinimum(); double leafMaximum = leaf.getPotentialMaximum(); for (AlternativeResultRange resultRange : resultRanges) { resultRange.setLowerBound(resultRange.getLowerBound() - ((resultRange.getResult() - leafMinimum) * leaf.getTotalWeight())); resultRange.setUpperBound(resultRange.getUpperBound() + ((leafMaximum - resultRange.getResult()) * leaf.getTotalWeight())); } } } /** * Takes a list of lower bounds and a list of corresponding upper bounds of * ranges and checks if there are overlapping ranges. * * Note: lowerBounds and upperBounds must have the same length * * @param resultRanges * the result ranges to check * @return true if at least one range overlaps another, false otherwise */ private boolean rangesOverlapping(List<AlternativeResultRange> resultRanges) { // Sort by upper bound Collections.sort(resultRanges, new Comparator<AlternativeResultRange>() { @Override public int compare(AlternativeResultRange o1, AlternativeResultRange o2) { return Double.compare(o1.getUpperBound(), o2.getUpperBound()); } }); double lastUpperBound = Double.NEGATIVE_INFINITY; for (AlternativeResultRange resultRange : resultRanges) { if (resultRange.getLowerBound() < lastUpperBound) { return true; } lastUpperBound = resultRange.getUpperBound(); } return false; } }