/*******************************************************************************
* Copyright 2006 - 2012 Vienna University of Technology,
* Department of Software Technology and Interactive Systems, IFS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package eu.scape_project.planning.criteria.bean;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import eu.scape_project.planning.model.TargetValueObject;
import eu.scape_project.planning.model.kbrowser.CriteriaLeaf;
import eu.scape_project.planning.model.kbrowser.VPlanLeaf;
import eu.scape_project.planning.model.measurement.Measure;
import eu.scape_project.planning.model.transform.NumericTransformer;
import eu.scape_project.planning.model.transform.OrdinalTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ImportanceAnalysis implements Serializable {
private static final long serialVersionUID = -814054847584527659L;
private static final Logger log = LoggerFactory.getLogger(ImportanceAnalysis.class);
private List<VPlanLeaf> planLeaves = new ArrayList<VPlanLeaf>();
private List<VPlanLeaf> mappedPlanLeaves = new ArrayList<VPlanLeaf>();
private List<PlanInfo> selectedPlans;
private long nrRelevantPlans;
private List<ImportanceAnalysisProperty> tableRows;
private int tableRowsCount = 0;
public ImportanceAnalysis(final Collection<Measure> measures, final List<VPlanLeaf> planLeaves,
final List<PlanInfo> selectedPlans) {
this.planLeaves = planLeaves;
filterMappedLeaves();
this.nrRelevantPlans = selectedPlans.size();
this.selectedPlans = selectedPlans;
buildTable(measures);
}
/**
* Method responsible for filtering mapped leaves out of all given leaves.
*/
private void filterMappedLeaves() {
// Filter mapped PlanLeaves
mappedPlanLeaves = new ArrayList<VPlanLeaf>(planLeaves.size());
for (VPlanLeaf pl : planLeaves) {
if (pl.isMapped()) {
mappedPlanLeaves.add(pl);
}
}
}
private void buildTable(Collection<Measure> measures) {
tableRows = new ArrayList<ImportanceAnalysisProperty>(measures.size());
for (Measure measure : measures) {
evaluateCriterion(measure);
}
tableRowsCount = tableRows.size();
}
private void evaluateCriterion(Measure measure) {
List<VPlanLeaf> criterionPlanLeaves = getCriterionPlanLeaves(measure);
if (criterionPlanLeaves.size() > 0) {
CriteriaLeaf criteriaLeaf = new CriteriaLeaf(nrRelevantPlans);
criteriaLeaf.setPlanLeaves(criterionPlanLeaves);
ImportanceAnalysisProperty importanceAnalysisProperty = new ImportanceAnalysisProperty();
importanceAnalysisProperty.setCategory(measure.getAttribute().getCategory().getName());
importanceAnalysisProperty.setAttribute(measure.getAttribute().getName());
importanceAnalysisProperty.setMeasure(measure.getName());
importanceAnalysisProperty.setIf1(criteriaLeaf.getImportanceFactorIF1());
importanceAnalysisProperty.setIf2(criteriaLeaf.getImportanceFactorIF2());
importanceAnalysisProperty.setIf3(criteriaLeaf.getImportanceFactorIF3());
importanceAnalysisProperty.setIf4(criteriaLeaf.getImportanceFactorIF4());
importanceAnalysisProperty.setIf5(criteriaLeaf.getImportanceFactorIF5());
importanceAnalysisProperty.setIf6(criteriaLeaf.getImportanceFactorIF6());
importanceAnalysisProperty.setIf7(criteriaLeaf.getImportanceFactorIF7());
importanceAnalysisProperty.setIf8(criteriaLeaf.getImportanceFactorIF8());
importanceAnalysisProperty.setIf9(criteriaLeaf.getImportanceFactorIF9());
importanceAnalysisProperty.setIf10(criteriaLeaf.getImportanceFactorIF10());
importanceAnalysisProperty.setIf11(criteriaLeaf.getImportanceFactorIF11());
importanceAnalysisProperty.setIf12(criteriaLeaf.getImportanceFactorIF12());
importanceAnalysisProperty.setIf13(criteriaLeaf.getImportanceFactorIF13());
importanceAnalysisProperty.setIf14(criteriaLeaf.getImportanceFactorIF14());
importanceAnalysisProperty.setIf15(criteriaLeaf.getImportanceFactorIF15());
importanceAnalysisProperty.setIf16(criteriaLeaf.getImportanceFactorIF16());
importanceAnalysisProperty.setIf17(criteriaLeaf.getImportanceFactorIF17());
importanceAnalysisProperty.setIf18(criteriaLeaf.getImportanceFactorIF18());
importanceAnalysisProperty.setIf19(calculateImportanceFactorIF19(selectedPlans, criteriaLeaf));
tableRows.add(importanceAnalysisProperty);
}
}
/**
* calculates importance factor "robustness" - the extend to which the
* measured value of a criterion can change, without impact on the winning
* alternative - CriteriaLeaf is an entity bean and part of plato-model for
* the calculations we need PlanInfo and I don't want to pollute plato-model
* (further) with kbrowser related classes. for this reason the calculation
* is done here.
*
* @param selectedPlans
* @param criteriaLeaf
* @return
*/
private double calculateImportanceFactorIF19(List<PlanInfo> selectedPlans, CriteriaLeaf criteriaLeaf) {
double sum = 0.0;
for (VPlanLeaf pLeaf : criteriaLeaf.getPlanLeaves()) {
String prefix = "IF19 - " + pLeaf.getMeasure().getUri() + ": ";
double leafFactor = 0.0;
PlanInfo planInfo = null;
// retrieve the PlanInfo for this plan
for (PlanInfo p : selectedPlans) {
if (p.getId() == pLeaf.getPlanId()) {
planInfo = p;
break;
}
}
Set<String> competitors = new HashSet<String>(planInfo.getOverallResults().getResults().keySet());
competitors.remove(planInfo.getWinningAlternative());
if (competitors.isEmpty()) {
// sum += leafFactor;
log.info(prefix + "No competitors left beside " + planInfo.getWinningAlternative() + " in plan: "
+ planInfo.getId());
continue;
}
double winnerOverallResult = planInfo.getWinningResult();
// determine result of the nearest non-winning alternative
String sndAlternative = "";
double sndOverallResult = Double.MIN_VALUE;
for (String a : competitors) {
double result = planInfo.getOverallResults().getResults().get(a);
if (result > sndOverallResult) {
sndOverallResult = result;
sndAlternative = a;
}
}
double overallDiffToNext = winnerOverallResult - sndOverallResult;
// map this difference back as target result for this criterion
double overallDiffToNextAsTargetValue = overallDiffToNext / pLeaf.getTotalWeight();
// get the transformed, but not weighted, result of the winning
// alternative for this criterion
double winnerTargetValue = pLeaf.getAlternativeResultsAsMap().get(planInfo.getWinningAlternative());
// calculate the minimal target value, which would cause a change of
// the winning alternative
double minTargetValue = winnerTargetValue - overallDiffToNextAsTargetValue;
// this will be negative, if the difference between 1st and 2nd is
// too big,
// and depending on the transformer setting, the minimal possible
// value could be > 0
String intermediaryResults = "; overallDiffToNext = " + overallDiffToNext + " = as target value = "
+ overallDiffToNextAsTargetValue + "; minTargetValue = " + minTargetValue
+ "; pLeaf.getPotentialMinimum() = " + pLeaf.getPotentialMinimum();
if (minTargetValue > pLeaf.getPotentialMinimum()) {
log.info(prefix + "1st " + planInfo.getWinningAlternative() + ", 2nd : " + sndAlternative
+ " in plan: " + planInfo.getId());
// this criterion could be a game changer - map it back to the
// measurement scale
if (pLeaf.getTransformer() instanceof NumericTransformer) {
NumericTransformer numTransformer = (NumericTransformer) pLeaf.getTransformer();
// For numeric values: The percentage that we can change the
// value on the (overally!) winning candidate
// without the output range changing so much as to make the
// winning candidate lose its winning rank
// TODO: CHECK: Do we have a problem here? the values of an
// alternative are first transformed, then aggregated
// therefore we have to transform this winning value also
// back ...
double winnerMeasuredValue = numTransformer.transformBack(winnerTargetValue);
double minMeasuredValue = numTransformer.transformBack(minTargetValue);
// TODO: what if the measured value was 0.0?
if (winnerMeasuredValue != 0.0) {
leafFactor = (winnerMeasuredValue - minMeasuredValue) / winnerMeasuredValue;
if (!numTransformer.hasIncreasingOrder()) {
leafFactor *= -1.0;
}
}
log.info(prefix + " - numeric = " + leafFactor + intermediaryResults + "; winnerMeasuredValue = "
+ winnerMeasuredValue + "; minMeasuredValue = " + minMeasuredValue + "; plan id "
+ planInfo.getId());
} else {
// it's an ordinal transformer
// For ordinals: Percentage of possible alternative values
// for the (overally!) winning candidate
// that would make the winning candidate lose its winning
// rank.
// E.g: “good, bad, ugly” > value is good; bad doesn’t
// change rank; ugly changes rank: robustness = 1/2 = 0.5
int numPossible = 0;
Collection<TargetValueObject> targetValueObjects = ((OrdinalTransformer) pLeaf.getTransformer())
.getMapping().values();
for (TargetValueObject value : targetValueObjects) {
if (value.getValue() >= minTargetValue) {
numPossible++;
}
}
leafFactor = (double) numPossible / (double) (targetValueObjects.size());
log.info(prefix + " - ordinal = " + leafFactor + intermediaryResults + "; numPossible = "
+ numPossible + "; numTotal = " + targetValueObjects.size() + "; plan id " + planInfo.getId());
}
} else {
// it's not possible to reduce the value of the winner to this
// amount
log.info(prefix + "1st " + planInfo.getWinningAlternative() + ", 2nd : " + sndAlternative
+ intermediaryResults + " in plan: " + planInfo.getId());
// leafFactor = 0.0
}
sum += leafFactor;
}
// TODO: CHECK: How to aggregate - just average values?
return sum / (double) criteriaLeaf.getPlanLeaves().size();
}
/**
* Method responsible for filtering all leaves which match the given
* criterion (Property [+ Metric]). Validity check of the input parameters
* (e.g. property + metric must be measurable) is done in calling code!!
*
* @param cMeasurableProperty
* property to filter.
* @param cMetric
* metric to filter.
*/
public List<VPlanLeaf> getCriterionPlanLeaves(Measure measure) {
List<VPlanLeaf> measurePlanLeaves = new ArrayList<VPlanLeaf>();
// filter PlanLeaves
for (VPlanLeaf l : mappedPlanLeaves) {
// property and metric must match
if (l.getMeasure().getUri().equals(measure.getUri())) {
measurePlanLeaves.add(l);
}
}
return measurePlanLeaves;
}
/**
* Exports the current importance table to a csv file.
*/
public void exportTableToCsv() {
}
/**
* Calculates the average potential output range based on the CPL
* occurrence.
*
* @param criterionPlanLeaves
* corresponding plan leaves.
* @return average potential output range based on the CPL occurrence, if
* calculation is possible. If calculation is not possible -1 is
* returned.
*/
/*
* public double
* getCPLAvgPotentialOutputRangeOccurrenceBased(List<VPlanLeaf>
* criterionPlanLeaves) { int appropriateLeaves = 0; double porSum = 0;
*
* for (VPlanLeaf l : criterionPlanLeaves) { double por =
* l.getPotentialOutputRange();
*
* // only leaves for which the por can be calculated can be used if (por !=
* -1) { porSum = porSum + por; appropriateLeaves++; } }
*
* if (appropriateLeaves == 0) { return -1; }
*
* double avgPor = porSum / appropriateLeaves; return avgPor; }
*/
/**
* Calculates the average actual output range based on the CPL occurrence.
*
* @param criterionPlanLeaves
* corresponding plan leaves.
* @return average actual output range based on the CPL occurrence, if
* calculation is possible. If calculation is not possible -1 is
* returned.
*/
/*
* public double getCPLAvgActualOutputRangeOccurrenceBased(List<VPlanLeaf>
* criterionPlanLeaves) { int appropriateLeaves = 0; double aorSum = 0;
*
* for (VPlanLeaf l : criterionPlanLeaves) { double aor =
* l.getActualOutputRange();
*
* // only leaves for which the aor can be calculated can be used if (aor !=
* -1) { aorSum = aorSum + aor; appropriateLeaves++; } }
*
* if (appropriateLeaves == 0) { return -1; }
*
* double avgAor = aorSum / appropriateLeaves; return avgAor; }
*/
public void setTableRows(List<ImportanceAnalysisProperty> tableRows) {
this.tableRows = tableRows;
}
public List<ImportanceAnalysisProperty> getTableRows() {
return tableRows;
}
public void setTableRowsCount(int tableRowsCount) {
this.tableRowsCount = tableRowsCount;
}
public int getTableRowsCount() {
return tableRowsCount;
}
/* ----------------- Init ----------------- */
public String init() {
log.debug("init finally called");
return "success";
}
}