/** * 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.z3.operator.henshin; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.henshin.interpreter.EGraph; import org.eclipse.emf.henshin.interpreter.Engine; import org.eclipse.emf.henshin.interpreter.Match; import org.eclipse.emf.henshin.interpreter.RuleApplication; import org.eclipse.emf.henshin.interpreter.impl.RuleApplicationImpl; import org.eclipse.emf.henshin.interpreter.util.InterpreterUtil; import org.eclipse.emf.henshin.model.Action; import org.eclipse.emf.henshin.model.Attribute; import org.eclipse.emf.henshin.model.Edge; import org.eclipse.emf.henshin.model.Formula; import org.eclipse.emf.henshin.model.HenshinFactory; import org.eclipse.emf.henshin.model.Module; import org.eclipse.emf.henshin.model.NestedCondition; import org.eclipse.emf.henshin.model.Node; import org.eclipse.emf.henshin.model.Rule; import org.eclipse.jdt.annotation.NonNull; import edu.toronto.cs.se.mavo.MAVOElement; import edu.toronto.cs.se.mmint.MMINTException; import edu.toronto.cs.se.mmint.mid.operator.impl.RandomOperatorImpl; import edu.toronto.cs.se.mmint.mid.utils.MIDOperatorIOUtils; import edu.toronto.cs.se.modelepedia.z3.Z3IncrementalSolver; import edu.toronto.cs.se.modelepedia.z3.Z3IncrementalSolver.Z3IncrementalBehavior; import edu.toronto.cs.se.modelepedia.z3.Z3Model; import edu.toronto.cs.se.modelepedia.z3.Z3Utils; public abstract class LiftingHenshinTransformation extends RandomOperatorImpl { protected class TransformationApplicabilityCondition { private Rule matchedRule; private Match match; private boolean isLiftedMatch; public TransformationApplicabilityCondition(Rule matchedRule, Match match, boolean isLiftedMatch) { this.matchedRule = matchedRule; this.match = match; this.isLiftedMatch = isLiftedMatch; } public Rule getMatchedRule() { return matchedRule; } public Match getMatch() { return match; } public boolean isLiftedMatch() { return isLiftedMatch; } } // input-output protected final static @NonNull String IN_MODEL = "original"; protected final static @NonNull String OUT_MODEL = "transformed"; protected final static @NonNull String OUT_MODELREL = "trace"; public static final String PROPERTY_IN_CONSTRAINT = "constraint"; private static final String PROPERTY_IN_CONSTRAINT_DEFAULT = Z3Utils.SMTLIB_TRUE; public static final String PROPERTY_IN_CONSTRAINTVARIABLES = "constraintVariables"; private static final String[] PROPERTY_IN_CONSTRAINTVARIABLES_DEFAULT = {}; private static final String PROPERTY_IN_TRANSFORMATIONMODULE = "transformationModule"; private static final String PROPERTY_IN_TRANSFORMATIONRULES = "transformationRules"; private static final String[] PROPERTY_IN_TRANSFORMATIONRULES_DEFAULT = {}; private static final String PROPERTY_IN_TRANSFORMATIONRULESLIFTING = "transformationRulesLifting"; private static final String PROPERTY_OUT_TIMECLASSICAL = "timeClassical"; protected static final String PROPERTY_OUT_TIMELIFTING = "timeLifting"; private static final String PROPERTY_OUT_RULEAPPLICATIONSCLASSICAL = "ruleApplicationsClassical"; protected static final String PROPERTY_OUT_RULEAPPLICATIONSLIFTING = "ruleApplicationsLifting"; private static final String PROPERTY_OUT_RULEAPPLICATIONSNOTLIFTING = "ruleApplicationsNotLifting"; protected static final String PROPERTY_OUT_SATCOUNTLIFTING = "satCountLifting"; protected static final String PROPERTY_OUT_UNSATCOUNTLIFTING = "unsatCountLifting"; protected static final String PROPERTY_OUT_SMTENCODINGLENGTH = "smtEncodingLength"; protected static final String PROPERTY_OUT_SMTENCODINGVARIABLES = "smtEncodingVariables"; protected static final String PROPERTY_OUT_TRANSFORMEDCONSTRAINT = "transformedConstraint"; private static final String PROPERTY_OUT_CHAINS = "chains"; private static final int PROPERTY_OUT_CHAINS_MAX = 10; private static final String PROPERTY_OUT_LITERALS = "literals"; private static final int PROPERTY_OUT_LITERALS_MAX = 10; // constants protected static final String ANAC_NAME = "A_NAC"; protected static final String TRANSFORMED_MODEL_SUFFIX = "_transformed"; protected static final String SMTLIB_APPLICABILITY_FUN = "(f"; protected static final String SMTLIB_APPLICABILITY_FUN_CONSTRAINTS = SMTLIB_APPLICABILITY_FUN + "X "; protected static final String SMTLIB_APPLICABILITY_FUN_APPLY = SMTLIB_APPLICABILITY_FUN + "Y "; protected static final String SMTLIB_APPLICABILITY_FUN_N = SMTLIB_APPLICABILITY_FUN + "N "; protected static final String SMTLIB_APPLICABILITY_FUN_C = SMTLIB_APPLICABILITY_FUN + "C "; protected static final String SMTLIB_APPLICABILITY_FUN_D = SMTLIB_APPLICABILITY_FUN + "D "; protected static final String SMTLIB_APPLICABILITY_FUN_A = SMTLIB_APPLICABILITY_FUN + "A "; protected String constraint; protected String[] constraintVariables; protected String transformationModule; protected String[] transformationRules; protected String[] transformationRulesLifting; protected List<Set<MAVOElement>> modelObjsNBar; protected Set<MAVOElement> modelObjsC; protected Set<MAVOElement> modelObjsD; protected Set<MAVOElement> modelObjsA; protected Set<MAVOElement> modelObjsCDN; protected int modelObjACounter; protected StringBuilder smtEncoding; protected Set<String> smtEncodingVariables; protected boolean timeClassicalEnabled; protected boolean transformedConstraintEnabled; protected long timeClassical; protected long timeLifting; protected int ruleApplicationsClassical; protected int ruleApplicationsLifting; protected int ruleApplicationsNotLifting; protected int satCountLifting; protected int unsatCountLifting; protected String transformedConstraint; protected Map<MAVOElement, Integer> modelObjsChains; protected Map<MAVOElement, Integer> modelObjsLiterals; @Override public void readInputProperties(Properties inputProperties) throws MMINTException { super.readInputProperties(inputProperties); constraint = MIDOperatorIOUtils.getOptionalStringProperty(inputProperties, PROPERTY_IN_CONSTRAINT, PROPERTY_IN_CONSTRAINT_DEFAULT); constraintVariables = MIDOperatorIOUtils.getOptionalStringProperties(inputProperties, PROPERTY_IN_CONSTRAINTVARIABLES, PROPERTY_IN_CONSTRAINTVARIABLES_DEFAULT); transformationModule = MIDOperatorIOUtils.getStringProperty(inputProperties, PROPERTY_IN_TRANSFORMATIONMODULE); transformationRules = MIDOperatorIOUtils.getOptionalStringProperties(inputProperties, PROPERTY_IN_TRANSFORMATIONRULES, PROPERTY_IN_TRANSFORMATIONRULES_DEFAULT); transformationRulesLifting = MIDOperatorIOUtils.getStringProperties(inputProperties, PROPERTY_IN_TRANSFORMATIONRULESLIFTING); timeClassicalEnabled = MIDOperatorIOUtils.getBoolProperty(inputProperties, PROPERTY_OUT_TIMECLASSICAL+MIDOperatorIOUtils.PROPERTY_IN_OUTPUTENABLED_SUFFIX); transformedConstraintEnabled = MIDOperatorIOUtils.getOptionalBoolProperty(inputProperties, PROPERTY_OUT_TRANSFORMEDCONSTRAINT+MIDOperatorIOUtils.PROPERTY_IN_OUTPUTENABLED_SUFFIX, false); } protected void writeProperties(Properties properties) { properties.setProperty(PROPERTY_OUT_TIMECLASSICAL, String.valueOf(timeClassical)); properties.setProperty(PROPERTY_OUT_TIMELIFTING, String.valueOf(timeLifting)); properties.setProperty(PROPERTY_OUT_RULEAPPLICATIONSCLASSICAL, String.valueOf(ruleApplicationsClassical)); properties.setProperty(PROPERTY_OUT_RULEAPPLICATIONSLIFTING, String.valueOf(ruleApplicationsLifting)); properties.setProperty(PROPERTY_OUT_RULEAPPLICATIONSNOTLIFTING, String.valueOf(ruleApplicationsNotLifting)); 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())); properties.setProperty(PROPERTY_OUT_TRANSFORMEDCONSTRAINT, transformedConstraint); int[] chains = new int[PROPERTY_OUT_CHAINS_MAX]; for (int chain : modelObjsChains.values()) { if (chain >= PROPERTY_OUT_CHAINS_MAX) { chains[PROPERTY_OUT_CHAINS_MAX-1]++; } else { chains[chain]++; } } for (int i = 0; i < chains.length; i++) { if (chains[i] > 0) { properties.setProperty(PROPERTY_OUT_CHAINS+i, String.valueOf(chains[i])); } } int[] literals = new int[PROPERTY_OUT_LITERALS_MAX]; for (int literal : modelObjsLiterals.values()) { if (literal >= PROPERTY_OUT_LITERALS_MAX) { literals[PROPERTY_OUT_LITERALS_MAX-1]++; } else { literals[literal]++; } } for (int i = 0; i < literals.length; i++) { if (literals[i] > 0) { properties.setProperty(PROPERTY_OUT_LITERALS+i, String.valueOf(literals[i])); } } } protected void init() { //state modelObjsNBar = new ArrayList<>(); modelObjsC = new HashSet<>(); modelObjsD = new HashSet<>(); modelObjsA = new HashSet<>(); modelObjsCDN = new HashSet<>(); modelObjACounter = 0; smtEncoding = new StringBuilder(); smtEncodingVariables = new HashSet<>(); // output timeClassical = -1; timeLifting = -1; ruleApplicationsClassical = 0; ruleApplicationsLifting = 0; ruleApplicationsNotLifting = 0; satCountLifting = 0; unsatCountLifting = 0; transformedConstraint = ""; modelObjsChains = new HashMap<>(); modelObjsLiterals = new HashMap<>(); } protected void initSMTEncoding(String preamble, String postamble) { for (String constraintVariable : constraintVariables) { smtEncodingVariables.add(constraintVariable); } for (String smtConstant: smtEncodingVariables) { smtEncoding.append(Z3Utils.constant(smtConstant, Z3Utils.SMTLIB_TYPE_BOOL)); } smtEncoding.append(preamble); smtEncoding.append(constraint); smtEncoding.append(postamble); } protected String createZ3ApplyFormulaMatchSet(Set<MAVOElement> modelObjs, String innerPredicate, String functionEmpty) { String smtEncoding = ""; if (modelObjs.isEmpty()) { smtEncoding += functionEmpty; } else { boolean simplify = (modelObjs.size() == 1) ? true : false; if (!simplify) { smtEncoding += innerPredicate; } for (MAVOElement modelObj : modelObjs) { smtEncoding += modelObj.getFormulaVariable() + " "; } smtEncoding = smtEncoding.substring(0, smtEncoding.length()-1); if (!simplify) { smtEncoding += Z3Utils.SMTLIB_PREDICATE_END; } } return smtEncoding; } protected void createZ3ApplyFormulaMatchSetIteration(Set<MAVOElement> modelObjs, String functionName, String innerPredicate, String functionEmpty) { smtEncoding.append( Z3Utils.assertion( Z3Utils.equality( Z3Utils.predicate(functionName, Integer.toString(ruleApplicationsLifting + 1)) + createZ3ApplyFormulaMatchSet(modelObjs, innerPredicate, functionEmpty) ) ) ); } protected void createZ3ApplyFormulaMatchSetNIteration() { smtEncoding.append(Z3Utils.SMTLIB_ASSERT); smtEncoding.append(Z3Utils.SMTLIB_EQUALITY); smtEncoding.append( Z3Utils.predicate(SMTLIB_APPLICABILITY_FUN_N, Integer.toString(ruleApplicationsLifting + 1)) ); if (modelObjsNBar.isEmpty()) { smtEncoding.append(Z3Utils.SMTLIB_FALSE); } else { boolean simplify = (modelObjsNBar.size() == 1) ? true : false; if (!simplify) { smtEncoding.append(Z3Utils.SMTLIB_OR); } boolean previousNSimplified = false; for (Set<MAVOElement> modelObjsN : modelObjsNBar) { if (previousNSimplified && modelObjsN.size() == 1) { smtEncoding.append(" "); } //TODO MMINT[LIFTING] review if true or false here when simplifying smtEncoding.append( createZ3ApplyFormulaMatchSet(modelObjsN, Z3Utils.SMTLIB_AND, Z3Utils.SMTLIB_FALSE) ); previousNSimplified = (modelObjsN.size() == 1) ? true : false; } if (!simplify) { smtEncoding.append(Z3Utils.SMTLIB_PREDICATE_END); } } smtEncoding.append(Z3Utils.SMTLIB_PREDICATE_END); smtEncoding.append(Z3Utils.SMTLIB_PREDICATE_END); } protected abstract void createZ3ApplyFormula(); protected boolean checkZ3ApplicabilityFormula(Z3IncrementalSolver z3IncSolver, int checkpointA) { int checkpointUnsat = smtEncoding.length(); createZ3ApplyFormula(); String applicabilityCondition = Z3Utils.assertion( Z3Utils.equality( Z3Utils.and( Z3Utils.predicate(SMTLIB_APPLICABILITY_FUN_CONSTRAINTS, Integer.toString(ruleApplicationsLifting)) + Z3Utils.predicate(SMTLIB_APPLICABILITY_FUN_APPLY, Integer.toString(ruleApplicationsLifting + 1)) ) + Z3Utils.SMTLIB_TRUE ) ); Z3Model z3ModelResult = z3IncSolver.checkSatAndGetModel(smtEncoding.substring(checkpointA) + applicabilityCondition, Z3IncrementalBehavior.POP_IF_UNSAT); if (z3ModelResult.getZ3Result().isSAT()) { satCountLifting++; return true; } smtEncoding.delete(checkpointUnsat, smtEncoding.length()); unsatCountLifting++; return false; } protected void updateChains() { int maxChain = 0; for (MAVOElement modelObjCDN : modelObjsCDN) { Integer modelObjCDNChain = modelObjsChains.get(modelObjCDN); if (modelObjCDNChain == null) { modelObjsChains.put(modelObjCDN, new Integer(0)); } else if (modelObjCDNChain > maxChain) { maxChain = modelObjCDNChain; } } maxChain++; for (MAVOElement modelObjA : modelObjsA) { modelObjsChains.put(modelObjA, new Integer(maxChain)); } } protected abstract void updateLiterals(); protected void getNNodesAndChangeToC(NestedCondition conditionN, Rule ruleN, Set<Node> nodesN) { // (N)ac nodes Map<Node, Node> forbid2preserve = new HashMap<Node, Node>(); for (Node nodeN : conditionN.getConclusion().getNodes()) { if (nodeN.getAction() != null && nodeN.getAction().getType() == Action.Type.FORBID) { Node newNodeN = HenshinFactory.eINSTANCE.createNode(); ruleN.getLhs().getNodes().add(newNodeN); nodesN.add(newNodeN); forbid2preserve.put(nodeN, newNodeN); newNodeN.setType(nodeN.getType()); // Action.Type.PRESERVE has to be set at last newNodeN.setAction(new Action(Action.Type.PRESERVE)); // copy attributes for (Attribute attributeN : nodeN.getAttributes()) { Attribute newAttributeN = HenshinFactory.eINSTANCE.createAttribute(); newNodeN.getAttributes().add(newAttributeN); newAttributeN.setType(attributeN.getType()); newAttributeN.setValue(attributeN.getValue()); newAttributeN.setAction(new Action(Action.Type.PRESERVE)); } } } for (Edge edgeN : conditionN.getConclusion().getEdges()) { if (edgeN.getAction() != null && edgeN.getAction().getType() == Action.Type.FORBID) { Edge newEdgeN = HenshinFactory.eINSTANCE.createEdge(); ruleN.getLhs().getEdges().add(newEdgeN); newEdgeN.setType(edgeN.getType()); Node newSrcNodeN = forbid2preserve.get(edgeN.getSource()); if (newSrcNodeN == null) { newSrcNodeN = conditionN.getMappings().getOrigin(edgeN.getSource()); } newEdgeN.setSource(newSrcNodeN); Node newTgtNodeN = forbid2preserve.get(edgeN.getTarget()); if (newTgtNodeN == null) { newTgtNodeN = conditionN.getMappings().getOrigin(edgeN.getTarget()); } newEdgeN.setTarget(newTgtNodeN); // Action.Type.PRESERVE has to be set at last newEdgeN.setAction(new Action(Action.Type.PRESERVE)); } } ruleN.getLhs().setFormula(null); } protected void getCDNodes(Rule rule, Set<Node> nodesC, Set<Node> nodesD) { for (Node node : rule.getLhs().getNodes()) { if (node.getAction() != null) { if (node.getAction().getType() == Action.Type.PRESERVE) { nodesC.add(node); } else if (node.getAction().getType() == Action.Type.DELETE) { nodesD.add(node); } } } } protected boolean areMatchesOverlapping(Match match1, Match match2, Set<Node> nodesC1, Set<Node> nodesC2, Set<Node> nodesD1, Set<Node> nodesD2) { // nodesC1 and nodesC2 are the same set of nodes by construction, but they could be from different copies of the same rule (same for nodesD1 and nodesD2) // the underlying model element instead is the same boolean same = (nodesC1 == nodesC2); Iterator<Node> iter1 = nodesC1.iterator(), iter2 = null; if (!same) { iter2 = nodesC2.iterator(); } while (iter1.hasNext()) { Node nodeC1 = iter1.next(); Node nodeC2 = (same) ? nodeC1 : iter2.next(); EObject nodeTargetC1 = match1.getNodeTarget(nodeC1); EObject nodeTargetC2 = match2.getNodeTarget(nodeC2); if (nodeTargetC1 != nodeTargetC2) { return false; } } iter1 = nodesD1.iterator(); if (!same) { iter2 = nodesD2.iterator(); } while (iter1.hasNext()) { Node nodeD1 = iter1.next(); Node nodeD2 = (same) ? nodeD1 : iter2.next(); EObject nodeTargetD1 = match1.getNodeTarget(nodeD1); EObject nodeTargetD2 = match2.getNodeTarget(nodeD2); if (nodeTargetD1 != nodeTargetD2) { return false; } } return true; } protected List<Match> findNMatches(Rule rule, Engine engine, EGraph graph, int indexN, Set<Node> nodesC, Set<Node> nodesD, Set<Node> nodesN) { NestedCondition conditionN = rule.getLhs().getNACs().get(indexN); rule.getLhs().setFormula((Formula) conditionN.eContainer()); // remove other Nacs getCDNodes(rule, nodesC, nodesD); getNNodesAndChangeToC(conditionN, rule, nodesN); return InterpreterUtil.findAllMatches(engine, rule, graph, null); } protected boolean addNBarModelObjs(Match matchN, Set<Node> nodesN) { Set<MAVOElement> modelObjsN = new HashSet<MAVOElement>(); getMatchedModelObjs(matchN, nodesN, modelObjsN, modelObjsCDN); boolean isLiftedMatchNBar = (modelObjsN.size() > 0); if (isLiftedMatchNBar) { modelObjsNBar.add(modelObjsN); } return isLiftedMatchNBar; } protected abstract void getMatchedModelObjs(Match match, Set<Node> nodes, Set<MAVOElement> modelObjs, Set<MAVOElement> allModelObjs); protected void matchAndTransformClassical(Rule rule, Engine engine, EGraph graph, boolean isLifting) { RuleApplication application = new RuleApplicationImpl(engine); application.setRule(rule); application.setEGraph(graph); for (Match match : engine.findMatches(rule, graph, null)) { application.setCompleteMatch(match); application.execute(null); if (isLifting) { ruleApplicationsNotLifting++; } else { ruleApplicationsClassical++; } } } protected void doTransformationClassical(Module module, Engine engine, EGraph graph) { long startTime = System.nanoTime(); for (String transformationRule : transformationRules) { Rule rule = (Rule) module.getUnit(transformationRule); matchAndTransformClassical(rule, engine, graph, false); } for (String transformationRuleLifted : transformationRulesLifting) { Rule rule = (Rule) module.getUnit(transformationRuleLifted); matchAndTransformClassical(rule, engine, graph, false); } timeClassical = System.nanoTime() - startTime; } protected void transformModelObjAWhenLifted(MAVOElement modelObjA) { // do nothing } protected void transformModelObjA(MAVOElement modelObjA) { // do nothing } protected void transformLifting(RuleApplication application, Match match, boolean isLiftedMatch) { // apply transformation application.setCompleteMatch(match); application.execute(null); // possibly propagate may to (A)dded elements Match resultMatch = application.getResultMatch(); for (EObject resultNodeTarget : resultMatch.getNodeTargets()) { if (!(resultNodeTarget instanceof MAVOElement)) { continue; } // (C)ontext/(D)eleted/(N)ac elements if (modelObjsCDN.contains(resultNodeTarget)) { continue; } // (A)dded elements if (isLiftedMatch) { modelObjsA.add((MAVOElement) resultNodeTarget); transformModelObjAWhenLifted((MAVOElement) resultNodeTarget); } transformModelObjA((MAVOElement) resultNodeTarget); modelObjACounter++; } } protected abstract int matchAndTransformLifting(Rule rule, Engine engine, EGraph graph, Z3IncrementalSolver z3IncSolver, int checkpointA); protected void doTransformationLifting(Module module, Engine engine, EGraph graph) { long startTime = System.nanoTime(); Z3IncrementalSolver z3IncSolver = new Z3IncrementalSolver(); z3IncSolver.firstCheckSatAndGetModel(smtEncoding.toString()); // run transformation rules marked as classical (e.g. root creation) for (String transformationRule : transformationRules) { Rule rule = (Rule) module.getUnit(transformationRule); matchAndTransformClassical(rule, engine, graph, true); } // run transformation rules marked as lifted int checkpointA = smtEncoding.length(); for (String transformationRuleLifted : transformationRulesLifting) { Rule rule = (Rule) module.getUnit(transformationRuleLifted); checkpointA = matchAndTransformLifting(rule, engine, graph, z3IncSolver, checkpointA); } timeLifting = System.nanoTime() - startTime; } }