/*******************************************************************************
* gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/
* Copyright (C) 2014 SVS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package userGeneratedContent.simulatorPlugIns.plugins.plotType;
import java.util.HashMap;
import staticContent.evaluation.simulator.core.statistics.ResultSet;
import staticContent.evaluation.simulator.core.statistics.aggregator.Aggregator;
import staticContent.evaluation.simulator.core.statistics.plotEngine.PlotScript;
import staticContent.evaluation.simulator.core.statistics.postProcessor.PostProcessor;
import userGeneratedContent.simulatorPlugIns.pluginRegistry.PlotType;
import userGeneratedContent.simulatorPlugIns.pluginRegistry.StatisticsType;
import userGeneratedContent.simulatorPlugIns.pluginRegistry.StatisticsType.Unit;
import gnu.trove.TDoubleArrayList;
public class MultiPlotter extends Plotter {
public enum PlotStyle {LINE_CHART_ABS, HISTOGRAM};
private final PlotStyle plotStyle;
public MultiPlotter(PlotStyle plotStyle) {
this.plotStyle = plotStyle;
}
@Override
public void plot(ResultSet resultSet) {
PlotType plotType;
if (this.plotStyle == PlotStyle.LINE_CHART_ABS) {
plotType = PlotType.LINE_CHART_ABS;
} else if (this.plotStyle == PlotStyle.HISTOGRAM) {
plotType = PlotType.HISTOGRAM;
} else {
throw new RuntimeException("ERROR: no case for PlotStyle " +this.plotStyle +" added yet.");
}
for (String plotName:resultSet.getDesiredPlots(plotType)) { // for each plot (= diagram to display)
StatisticsType[] desiredEvaluations = resultSet.getDesiredEvaluations(plotName);
this.checkValidity(desiredEvaluations);
PlotScript plotScript = new PlotScript(plotName, resultSet);
String plotCommand = "plot";
String plotstyle = (resultSet.getNumberOfValidationRuns() == 0) ? "linespoints" : "yerrorlines";
int columnCounter; // for plot command
if (this.plotStyle == PlotStyle.LINE_CHART_ABS) {
columnCounter = 1;
} else if (this.plotStyle == PlotStyle.HISTOGRAM) {
columnCounter = 0;
} else {
throw new RuntimeException("ERROR: no case for PlotStyle " +this.plotStyle +" added yet.");
}
if (resultSet.ep.values.length > 1) {
plotName +=" - effect of " + resultSet.ep.propertyToVary;
}
if (!resultSet.ep.isNumeric) {
plotName += " (";
for (int i=0; i<resultSet.ep.values.length ; i++) {
double id = resultSet.ep.propertyToVaryToId.get(resultSet.ep.values[i]);
plotName += ""+decimalFormat.format(id) +"=" +resultSet.ep.values[i];
if (i <(resultSet.ep.values.length-1)) {
plotName += ",";
}
}
plotName += ")";
}
plotScript.setTitle(plotName);
plotScript.setXlabel(resultSet.ep.propertyToVary);
plotScript.setYlabel(desiredEvaluations[0].unitAsString);
plotScript.setScale(desiredEvaluations[0].plotScale);
if (this.plotStyle == PlotStyle.HISTOGRAM) {
plotScript.setNoneOverwritableParameter("set style data histogram");
plotScript.setNoneOverwritableParameter("set style histogram cluster gap 1");
if (resultSet.getNumberOfValidationRuns() > 0) {
plotScript.setNoneOverwritableParameter("set style histogram errorbars linewidth 1");
}
plotScript.setNoneOverwritableParameter("set style fill solid border -1");
plotScript.setNoneOverwritableParameter("set boxwidth 0.7");
plotScript.setNoneOverwritableParameter("set bars front");
plotScript.setNoneOverwritableParameter("set key outside center bottom samplen 0");
}
StringBuffer resultFileContent = new StringBuffer(10000);
String aggregatorDescription = "";
String plotLineTitle = "";
for (int i=0; i<resultSet.ep.values.length; i++) { // for each varying_value
// calculate results:
if (resultSet.ep.isNumeric) {
resultFileContent.append(resultSet.ep.values[i]);
} else {
resultFileContent.append(resultSet.ep.propertyToVaryToId.get(resultSet.ep.values[i]));
}
for (StatisticsType statisticsType:desiredEvaluations) { // for each StatisticsType
this.checkValidity(statisticsType, resultSet, i);
HashMap<Aggregator, TDoubleArrayList> resultsForAllRuns = resultSet.getResultsForAllRuns(i, statisticsType);
for (Aggregator ag:resultsForAllRuns.keySet()) { // for each aggregation type (= each line in the plot)
if (i == 0) {
aggregatorDescription = (ag.name().equals(statisticsType.sourceValueAggregator.name())) ? ag.name() : ag.name() +" " +statisticsType.sourceValueAggregator.name();
plotLineTitle = aggregatorDescription +" (" +statisticsType.name() +")";
}
TDoubleArrayList resultForAggregator = resultsForAllRuns.get(ag);
if (resultForAggregator.size() == 1) { // no validation runs
double result = this.performPostProcessing(resultForAggregator.get(0), statisticsType, resultSet, i, 0);
resultFileContent.append(" " +decimalFormat.format(result));
if (i == 0) {
if (this.plotStyle == PlotStyle.LINE_CHART_ABS) {
plotCommand += " varInputFile using 1:" + (++columnCounter) + " w " +plotstyle +" title '" +plotLineTitle + "',";
} else if (this.plotStyle == PlotStyle.HISTOGRAM) {
plotCommand += " varInputFile using " +(++columnCounter +1) +":xtic(1) title '" +plotLineTitle + "',";
} else {
throw new RuntimeException("ERROR: no case for PlotStyle " +this.plotStyle +" added yet.");
}
}
} else { // validation runs
double[] result = new double[3]; // 0:avg, 1:min, 2:max
resultForAggregator = this.performPostProcessing(resultForAggregator, statisticsType, resultSet, i);
result[0] = Aggregator.AVG.aggregate(resultForAggregator);
result[1] = Aggregator.MIN.aggregate(resultForAggregator);
result[2] = Aggregator.MAX.aggregate(resultForAggregator);
resultFileContent.append(" " +decimalFormat.format(result[0]) +" " +decimalFormat.format(result[1]) +" " +decimalFormat.format(result[2]));
if (i == 0) {
if (this.plotStyle == PlotStyle.LINE_CHART_ABS) {
plotCommand += " varInputFile using 1:" +(++columnCounter) +":" +(++columnCounter) +":" +(++columnCounter) +" w " +plotstyle +" title '" +plotLineTitle + "',";
} else if (this.plotStyle == PlotStyle.HISTOGRAM) {
plotCommand += " varInputFile using " +(++columnCounter +1) +":" +(++columnCounter +1) +":" +(++columnCounter +1) +":xtic(1) title '" +plotLineTitle + "',";
} else {
throw new RuntimeException("ERROR: no case for PlotStyle " +this.plotStyle +" added yet.");
}
}
}
}
}
resultFileContent.append("\n");
}
resultFileContent.append("\n");
plotScript.setPlotCommand(plotCommand);
plotScript.writeDataFileToDisk(resultFileContent.toString());
plotScript.writePlotScriptToDisk();
plotScript.plot();
}
}
private void checkValidity(StatisticsType[] desiredEvaluations) {
Unit lastUnit = null;
for (StatisticsType st: desiredEvaluations) {
if (lastUnit == null) {
lastUnit = st.unit;
} else {
if (lastUnit != st.unit) {
throw new RuntimeException("ERROR: PlotType." +this.plotStyle +" " +
"requires all lines of a plot to have the same unit. "
+st +" in plot " +st.destinationPlot +" has a different " +
"unit. Make " +"sure to set it to " +lastUnit +", or select " +
"another destinationPlot for " +st +" in StatisticsType.java.");
}
}
}
}
private void checkValidity(StatisticsType statisticsType, ResultSet resultSet, int varyingValueId) {
if (statisticsType.sourceAggregators[0] == Aggregator.NONE) {
System.err.println("WARNING: PlotType." +this.plotStyle +" requires at least one SourceAggregator, " +
"but no SourceAggregator is specified for StatisticsType " +statisticsType +".\n" +
"Will use the default SourceAggregator: AVG");
statisticsType.sourceAggregators[0] = Aggregator.AVG;
} else {
for (PostProcessor pp:statisticsType.postProcessors) {
if (pp == PostProcessor.SORT) {
System.err.println("WARNING: PlotType." +this.plotStyle +" does not support sorting, but SORT is " +
"selected as PostProcessor for StatisticsType " +statisticsType +".\n" +
"Will ignore the SORT command.");
}
}
}
if (!resultSet.containsData(varyingValueId, statisticsType, 0)) {
System.err.println("WARNING: No results recorded for " +statisticsType +".\nWill set result to 0.0.");
}
}
private TDoubleArrayList performPostProcessing(TDoubleArrayList values, StatisticsType statisticsType, ResultSet resultSet, int varyingValueId) {
for (int i=0; i<values.size(); i++) {
values.set(i, this.performPostProcessing(values.get(i), statisticsType, resultSet, varyingValueId, i));
}
return values;
}
private double performPostProcessing(double value, StatisticsType statisticsType, ResultSet resultSet, int varyingValueId, int runId) {
if (statisticsType.postProcessors[0] != PostProcessor.NONE) {
for (PostProcessor pp:statisticsType.postProcessors) {
if (pp == PostProcessor.SORT) {
continue;
}
value = pp.process(value, resultSet, varyingValueId, runId);
}
}
if (statisticsType.unit == Unit.kbyte) {
value = value / 1024d;
} else if (statisticsType.unit == Unit.mbyte) {
value = value / (1024d * 1024d);
} else if (statisticsType.unit == Unit.gbyte) {
value = value / (1024d * 1024d * 1024d);
} else if (statisticsType.unit == Unit.sec) {
value = value / 1000d;
}
return value;
}
}