/**
* Copyright (c) 2012-2016 Marsha Chechik, Alessio Di Sandro, Michalis Famelis,
* Rick Salay.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Alessio Di Sandro - Implementation.
*/
package edu.toronto.cs.se.modelepedia.classdiagram_mavo.operator;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import edu.toronto.cs.se.mmint.MMINT;
import edu.toronto.cs.se.mmint.MMINTException;
import edu.toronto.cs.se.mavo.MAVOElement;
import edu.toronto.cs.se.mmint.mid.GenericElement;
import edu.toronto.cs.se.mmint.mid.MID;
import edu.toronto.cs.se.mmint.mid.Model;
import edu.toronto.cs.se.mmint.mid.utils.FileUtils;
import edu.toronto.cs.se.mmint.mid.utils.MIDOperatorIOUtils;
import edu.toronto.cs.se.modelepedia.classdiagram_mavo.ClassDiagram_MAVOFactory;
import edu.toronto.cs.se.modelepedia.operator.experiment.ExperimentDriver;
import edu.toronto.cs.se.modelepedia.z3.Z3IncrementalSolver;
import edu.toronto.cs.se.modelepedia.z3.Z3Utils;
import edu.toronto.cs.se.modelepedia.z3.operator.henshin.ProductLineHenshinTransformation;
public class ICSE14 extends ProductLineHenshinTransformation {
// input-output
private final static @NonNull String IN_MODEL = "cd";
private static final String PROPERTY_IN_FEATUREMODELNAME = "featureModelName";
private static final String PROPERTY_IN_NUMRULEELEMENTS = "numRuleElements";
private static final String PROPERTY_IN_NUMRULEELEMENTS_SEPARATOR = "-";
private static final String PROPERTY_IN_MODELSIZE = "modelSize";
private static final String PROPERTY_IN_MAXCHAINS = "maxChains";
private static final String PROPERTY_IN_NUMITERATIONS = "numIterations";
private static final String PROPERTY_IN_NACMATCHPERC = "nacMatchPerc";
private static final double PROPERTY_IN_NACMATCHPERC_DEFAULT = 0.5;
private static final String PROPERTY_IN_ALWAYSPRESENTPERC = "alwaysPresentPerc";
private static final double PROPERTY_IN_ALWAYSPRESENTPERC_DEFAULT = 0.5;
// constants
private static final String FEATURE_MODELS_SUBDIR = "featuremodels";
private String featureModelName;
private int numRuleElementsN;
private int numRuleElementsC;
private int numRuleElementsA;
private int modelSize;
private int maxChains;
private int numIterations;
private double nacMatchPerc;
private double alwaysPresentPerc;
private Model inputModel;
private List<MAVOElement> modelObjsBucketA;
private List<Integer> modelObjsChainsA;
@Override
public void readInputProperties(Properties inputProperties) throws MMINTException {
featureModelName = MIDOperatorIOUtils.getStringProperty(inputProperties, PROPERTY_IN_FEATUREMODELNAME);
Properties constraintProperties = new Properties();
String constraintPropertiesFile = FileUtils.prependWorkspacePathToUri(
FileUtils.replaceLastSegmentInUri(
inputModel.getUri(),
FEATURE_MODELS_SUBDIR + MMINT.URI_SEPARATOR + featureModelName + MIDOperatorIOUtils.PROPERTIES_SUFFIX
)
);
try {
constraintProperties.load(new FileInputStream(constraintPropertiesFile));
}
catch (Exception e) {
}
constraint = MIDOperatorIOUtils.getStringProperty(constraintProperties, PROPERTY_IN_CONSTRAINT);
constraintVariables = MIDOperatorIOUtils.getStringProperties(constraintProperties, PROPERTY_IN_CONSTRAINTVARIABLES);
String[] numRuleElements = MIDOperatorIOUtils.getStringProperty(inputProperties, PROPERTY_IN_NUMRULEELEMENTS).split(PROPERTY_IN_NUMRULEELEMENTS_SEPARATOR);
numRuleElementsN = Integer.parseInt(numRuleElements[0]);
numRuleElementsC = Integer.parseInt(numRuleElements[1]);
numRuleElementsA = Integer.parseInt(numRuleElements[2]);
modelSize = MIDOperatorIOUtils.getIntProperty(inputProperties, PROPERTY_IN_MODELSIZE);
maxChains = MIDOperatorIOUtils.getIntProperty(inputProperties, PROPERTY_IN_MAXCHAINS);
numIterations = MIDOperatorIOUtils.getIntProperty(inputProperties, PROPERTY_IN_NUMITERATIONS);
nacMatchPerc = MIDOperatorIOUtils.getOptionalDoubleProperty(inputProperties, PROPERTY_IN_NACMATCHPERC, PROPERTY_IN_NACMATCHPERC_DEFAULT);
alwaysPresentPerc = MIDOperatorIOUtils.getOptionalDoubleProperty(inputProperties, PROPERTY_IN_ALWAYSPRESENTPERC, PROPERTY_IN_ALWAYSPRESENTPERC_DEFAULT);
}
protected void writeProperties(Properties properties) {
properties.setProperty(PROPERTY_OUT_TIMELIFTING, String.valueOf(timeLifting));
properties.setProperty(PROPERTY_OUT_SATCOUNTLIFTING, String.valueOf(satCountLifting));
properties.setProperty(PROPERTY_OUT_UNSATCOUNTLIFTING, String.valueOf(unsatCountLifting));
properties.setProperty(PROPERTY_OUT_SMTENCODINGLENGTH, String.valueOf(smtEncoding.length()));
properties.setProperty(PROPERTY_OUT_SMTENCODINGVARIABLES, String.valueOf(smtEncodingVariables.size()));
}
@Override
protected void init() {
super.init();
// state
modelObjsBucketA = new ArrayList<>();
modelObjsChainsA = new ArrayList<>();
}
private void transformMatch() {
modelSize += numRuleElementsA;
for (int i = 0; i < numRuleElementsA; i++) {
MAVOElement modelObjA = ClassDiagram_MAVOFactory.eINSTANCE.createClass();
modelObjA.setFormulaVariable(SMTLIB_APPLICABILITY_FUN_APPLY + (ruleApplicationsLifting+1) + Z3Utils.SMTLIB_PREDICATE_END);
modelObjsBucketA.add(modelObjA);
modelObjsChainsA.add(new Integer(maxChains));
}
}
private boolean checkApplicabilityConditions(Z3IncrementalSolver z3IncSolver) {
modelObjsNBar.clear();
modelObjsC.clear();
modelObjsD.clear();
Set<MAVOElement> modelObjsN = new HashSet<MAVOElement>();
modelObjsNBar.add(modelObjsN);
double modelObjAMatchPerc = modelObjsChainsA.size() / modelSize;
boolean nacMatched = (state.nextDouble() < nacMatchPerc);
for (int i = 0; i < (numRuleElementsN+numRuleElementsC); i++) {
MAVOElement modelObj = null;
boolean modelObjAMatched = (state.nextDouble() < modelObjAMatchPerc);
if (modelObjAMatched) { // previously (A)dded element matched
int indexA = state.nextInt(modelObjsChainsA.size());
int chains = modelObjsChainsA.get(indexA);
if (chains > 0) { // still able to chain
chains--;
if (chains == 0) {
modelObj = modelObjsBucketA.remove(indexA);
modelObjsChainsA.remove(indexA);
}
else {
modelObj = modelObjsBucketA.get(indexA);
modelObjsChainsA.add(indexA, new Integer(chains));
}
}
}
else {
modelObj = ClassDiagram_MAVOFactory.eINSTANCE.createClass();
String formulaId = (state.nextDouble() < alwaysPresentPerc) ?
Z3Utils.SMTLIB_TRUE :
constraintVariables[state.nextInt(constraintVariables.length)];
modelObj.setFormulaVariable(formulaId);
}
if (i < numRuleElementsC) { // (C)ontext element matched
modelObjsC.add(modelObj);
}
else {
if (nacMatched) { // (N)ac element matched
modelObjsN.add(modelObj);
}
else {
break;
}
}
}
return checkZ3ApplicabilityFormula(z3IncSolver, smtEncoding.length());
}
private void doSimulatedLifting(Z3IncrementalSolver z3IncSolver) throws MMINTException {
long startTime = System.nanoTime();
while (ruleApplicationsLifting < numIterations) {
checkApplicabilityConditions(z3IncSolver);
modelObjsA.clear();
transformMatch();
ruleApplicationsLifting++;
}
timeLifting = System.nanoTime() - startTime;
}
@Override
public Map<String, Model> run(
Map<String, Model> inputsByName, Map<String, GenericElement> genericsByName,
Map<String, MID> outputMIDsByName) throws Exception {
// input
inputModel = inputsByName.get(IN_MODEL);
this.init();
super.initSMTEncoding(SMTLIB_APPLICABILITY_PREAMBLE, SMTLIB_APPLICABILITY_POSTAMBLE);
Z3IncrementalSolver z3IncSolver = new Z3IncrementalSolver();
z3IncSolver.firstCheckSatAndGetModel(smtEncoding.toString());
doSimulatedLifting(z3IncSolver);
// output
Properties outputProperties = new Properties();
writeProperties(outputProperties);
MIDOperatorIOUtils.writePropertiesFile(
outputProperties,
this,
inputModel,
getInputSubdir(),
MIDOperatorIOUtils.OUTPUT_PROPERTIES_SUFFIX
);
return new HashMap<>();
}
private static class DatLine implements Comparable<DatLine> {
public static final Map<String, Integer> TIMELIFTING_NUMRULEELEMENTS_INDEXES = new HashMap<String, Integer>();
static {
TIMELIFTING_NUMRULEELEMENTS_INDEXES.put("1-2-1", new Integer(0));
TIMELIFTING_NUMRULEELEMENTS_INDEXES.put("7-2-7", new Integer(1));
TIMELIFTING_NUMRULEELEMENTS_INDEXES.put("3-2-3", new Integer(2));
TIMELIFTING_NUMRULEELEMENTS_INDEXES.put("30-9-24", new Integer(3));
TIMELIFTING_NUMRULEELEMENTS_INDEXES.put("1-12-1", new Integer(4));
TIMELIFTING_NUMRULEELEMENTS_INDEXES.put("6-19-6", new Integer(5));
TIMELIFTING_NUMRULEELEMENTS_INDEXES.put("16-21-16", new Integer(6));
}
public double smtEncodingVariables;
public double[] timeLifting_numRuleElements;
public DatLine() {
this.timeLifting_numRuleElements = new double[TIMELIFTING_NUMRULEELEMENTS_INDEXES.size()];
}
@Override
public int compareTo(DatLine other) {
if (smtEncodingVariables < other.smtEncodingVariables) {
return -1;
}
else if (smtEncodingVariables > other.smtEncodingVariables) {
return 1;
}
return 0;
}
}
private static final String EXPERIMENT_OUTPUT_FILE = "ExperimentDriverOut.properties";
private static final String GNUPLOT_OUTPUT_FILE = "gnuplot.dat";
private static Map<String, DatLine> datLinesMap;
private static void getOutput(Path outputPath) throws Exception {
Properties outputProperties = new Properties();
outputProperties.load(new FileInputStream(outputPath.toString()));
String featureModelName = MIDOperatorIOUtils.getStringProperty(outputProperties, PROPERTY_IN_FEATUREMODELNAME+ExperimentDriver.PROPERTY_OUT_VARIABLEINSTANCE_SUFFIX);
double smtEncodingVariables = MIDOperatorIOUtils.getDoubleProperty(outputProperties, PROPERTY_OUT_SMTENCODINGVARIABLES+ExperimentDriver.PROPERTY_OUT_RESULTAVG_SUFFIX);
String numRuleElements = MIDOperatorIOUtils.getStringProperty(outputProperties, PROPERTY_IN_NUMRULEELEMENTS+ExperimentDriver.PROPERTY_OUT_VARIABLEINSTANCE_SUFFIX);
double timeLifting = MIDOperatorIOUtils.getDoubleProperty(outputProperties, PROPERTY_OUT_TIMELIFTING+ExperimentDriver.PROPERTY_OUT_RESULTAVG_SUFFIX);
// double timeLifting = MultiModelOperatorUtils.getDoubleProperty(outputProperties, PROPERTY_OUT_UNSATCOUNTLIFTING+ExperimentDriver.PROPERTY_OUT_RESULTAVG_SUFFIX);
DatLine datLine = datLinesMap.get(featureModelName);
if (datLine == null) {
datLine = new DatLine();
datLinesMap.put(featureModelName, datLine);
}
datLine.smtEncodingVariables = smtEncodingVariables;
datLine.timeLifting_numRuleElements[DatLine.TIMELIFTING_NUMRULEELEMENTS_INDEXES.get(numRuleElements)] = timeLifting;
}
private static void createGnuplotFile(Path outputDirectory, List<DatLine> datLines) {
Path outputFile = outputDirectory.resolve(GNUPLOT_OUTPUT_FILE);
try (BufferedWriter writer = Files.newBufferedWriter(outputFile, Charset.forName("UTF-8"))) {
double prevSmtEncodingVariables = datLines.get(0).smtEncodingVariables;
double[] prevTotals = new double[DatLine.TIMELIFTING_NUMRULEELEMENTS_INDEXES.size()];
for (int i = 0; i < prevTotals.length; i++) {
prevTotals[i] = 0;
}
int prevCount = 0;
for (DatLine datLine : datLines) {
if (prevSmtEncodingVariables == datLine.smtEncodingVariables) {
for (int i = 0; i < prevTotals.length; i++) {
prevTotals[i] += datLine.timeLifting_numRuleElements[i];
}
prevCount++;
}
else {
writer.write(Double.toString(prevSmtEncodingVariables));
writer.write(" ");
for (int i = 0; i < prevTotals.length; i++) {
writer.write(Double.toString(prevTotals[i]/prevCount/1000000000));
// writer.write(Double.toString(prevTotals[i]/prevCount));
writer.write(" ");
prevTotals[i] = datLine.timeLifting_numRuleElements[i];
}
writer.newLine();
prevSmtEncodingVariables = datLine.smtEncodingVariables;
prevCount = 1;
}
}
writer.write(Double.toString(prevSmtEncodingVariables));
writer.write(" ");
for (int i = 0; i < prevTotals.length; i++) {
writer.write(Double.toString(prevTotals[i]/prevCount/1000000000));
// writer.write(Double.toString(prevTotals[i]/prevCount));
writer.write(" ");
}
writer.newLine();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String inputPath = args[0];
Path path = Paths.get(inputPath);
if (!Files.isDirectory(path)) {
return;
}
datLinesMap = new HashMap<String, DatLine>();
FileVisitor<Path> visitor = new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (!file.getFileName().toString().equals(EXPERIMENT_OUTPUT_FILE)) {
return FileVisitResult.CONTINUE;
}
try {
getOutput(file);
}
catch (Exception e) {
e.printStackTrace();
}
return FileVisitResult.CONTINUE;
}
};
try {
Files.walkFileTree(path, visitor);
List<DatLine> datLines = new ArrayList<DatLine>(datLinesMap.values());
Collections.sort(datLines);
createGnuplotFile(path, datLines);
} catch (IOException e) {
e.printStackTrace();
}
}
}