/** * 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.graph_mavo.operator; import java.util.ArrayList; 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.emf.common.util.EList; import org.eclipse.jdt.annotation.NonNull; import edu.toronto.cs.se.mmint.MMINTException; import edu.toronto.cs.se.mmint.mavo.constraint.MAVOMIDConstraintChecker; import edu.toronto.cs.se.mmint.mavo.library.MAVOUtils; import edu.toronto.cs.se.mmint.mavo.reasoning.IMAVOReasoningEngine.MAVOTruthValue; import edu.toronto.cs.se.mavo.MAVOElement; import edu.toronto.cs.se.mavo.MAVORoot; 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.operator.Operator; 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.graph_mavo.Edge; import edu.toronto.cs.se.modelepedia.graph_mavo.Graph; import edu.toronto.cs.se.modelepedia.graph_mavo.Node; import edu.toronto.cs.se.modelepedia.z3.Z3Model; import edu.toronto.cs.se.modelepedia.z3.Z3IncrementalSolver; import edu.toronto.cs.se.modelepedia.z3.Z3IncrementalSolver.Z3IncrementalBehavior; import edu.toronto.cs.se.modelepedia.z3.Z3Utils; import edu.toronto.cs.se.modelepedia.z3.Z3Model.Z3Result; import edu.toronto.cs.se.modelepedia.z3.mavo.Z3MAVOModelParser; import edu.toronto.cs.se.modelepedia.z3.reasoning.Z3ReasoningEngine; import edu.toronto.cs.se.modelepedia.z3.reasoning.Z3ReasoningEngine.MAVOCheckStrategy; public class TOSEM12 extends RandomOperatorImpl { // input-output private final static @NonNull String IN_MODEL = "model"; private static final String PROPERTY_IN_NUMCONCRETIZATIONS = "numConcretizations"; private static final String PROPERTY_IN_PROPERTYID = "propertyId"; private static final String PROPERTY_OUT_TIMEMAVO = "timeMAVO"; private static final String PROPERTY_OUT_TIMECLASSICAL = "timeClassical"; private static final String PROPERTY_OUT_TIMEMAVOBACKBONE = "timeMAVOBackbone"; private static final String PROPERTY_OUT_TIMEMAVOALLSAT = "timeMAVOAllsat"; private static final String PROPERTY_OUT_SPEEDUPCLASSICALMAVO = "speedupClassicalMAVO"; private static final String PROPERTY_OUT_SPEEDUPMAVOALLSATMAVOBACKBONE = "speedupMAVOAllsatMAVOBackbone"; // constants private static final String Z3_LANGUAGE = "SMTLIB"; // input private int numConcretizations; private int propertyId; private boolean timeClassicalEnabled; private boolean timeMAVOBackboneEnabled; private boolean timeMAVOAllsatEnabled; // state private Map<String, MAVOElement> mayModelObjs; private String smtEncoding; private String smtConcretizationsConstraint; private String smtProperty; private String smtEncodingAndConcretizations; private Set<String> smtConcretizations; Z3ReasoningEngine z3Reasoner; Z3MAVOModelParser z3ModelParser; // output private long timeMAVO; private long timeClassical; private long timeMAVOBackbone; private long timeMAVOAllsat; private double speedupClassicalMAVO; private double speedupMAVOAllsatMAVOBackbone; @Override public void readInputProperties(Properties inputProperties) throws MMINTException { super.readInputProperties(inputProperties); numConcretizations = MIDOperatorIOUtils.getOptionalIntProperty(inputProperties, PROPERTY_IN_NUMCONCRETIZATIONS, 1); propertyId = MIDOperatorIOUtils.getOptionalIntProperty(inputProperties, PROPERTY_IN_PROPERTYID, 0); timeClassicalEnabled = MIDOperatorIOUtils.getOptionalBoolProperty(inputProperties, PROPERTY_OUT_TIMECLASSICAL+MIDOperatorIOUtils.PROPERTY_IN_OUTPUTENABLED_SUFFIX, false); timeMAVOBackboneEnabled = MIDOperatorIOUtils.getOptionalBoolProperty(inputProperties, PROPERTY_OUT_TIMEMAVOBACKBONE+MIDOperatorIOUtils.PROPERTY_IN_OUTPUTENABLED_SUFFIX, false); timeMAVOAllsatEnabled = MIDOperatorIOUtils.getOptionalBoolProperty(inputProperties, PROPERTY_OUT_TIMEMAVOALLSAT+MIDOperatorIOUtils.PROPERTY_IN_OUTPUTENABLED_SUFFIX, false); } private MAVORoot init(Model mayModel) throws Exception { // output timeMAVO = -1; timeClassical = -1; timeMAVOBackbone = -1; timeMAVOAllsat = -1; speedupClassicalMAVO = -1; speedupMAVOAllsatMAVOBackbone = -1; // state z3Reasoner = (Z3ReasoningEngine) MAVOMIDConstraintChecker.getMAVOReasoner(Z3_LANGUAGE); z3ModelParser = z3Reasoner.generateSMTLIBEncoding(mayModel); smtEncoding = z3ModelParser.getSMTLIBEncoding(); MAVORoot rootMayModelObj = (MAVORoot) mayModel.getEMFInstanceRoot(); Operator previousOperator = getPreviousOperator(); // GenerateRandomGraphMAVO if (previousOperator != null) { mayModelObjs = ((GenerateRandomGraphMAVO) previousOperator).getMAVOModelObjects(); generateSMTLIBGroundedProperty((Graph) rootMayModelObj); generateRandomSMTLIBConcretizations(); smtEncodingAndConcretizations = smtEncoding + Z3Utils.assertion(smtConcretizationsConstraint); } else { timeMAVOAllsatEnabled = true; mayModelObjs = MAVOUtils.getAnnotatedMAVOModelObjects(mayModel); smtProperty = mayModel.getConstraint().getImplementation(); smtEncodingAndConcretizations = smtEncoding; generateAllsatSMTLIBConcretizations(rootMayModelObj); } return rootMayModelObj; } private void writeProperties(Properties properties) { properties.setProperty(PROPERTY_OUT_TIMEMAVO, String.valueOf(timeMAVO)); properties.setProperty(PROPERTY_OUT_TIMECLASSICAL, String.valueOf(timeClassical)); properties.setProperty(PROPERTY_OUT_TIMEMAVOBACKBONE, String.valueOf(timeMAVOBackbone)); properties.setProperty(PROPERTY_OUT_TIMEMAVOALLSAT, String.valueOf(timeMAVOAllsat)); properties.setProperty(PROPERTY_OUT_SPEEDUPCLASSICALMAVO, String.valueOf(speedupClassicalMAVO)); properties.setProperty(PROPERTY_OUT_SPEEDUPMAVOALLSATMAVOBACKBONE, String.valueOf(speedupMAVOAllsatMAVOBackbone)); } private String generateRandomSMTLIBConcretization() { String smtConcretization = ""; Map<String, Boolean> wellFormedModelObjs = new HashMap<String, Boolean>(); for (MAVOElement mayModelObj : mayModelObjs.values()) { String mayModelObjSmtEncoding = (mayModelObj instanceof Node) ? Z3Utils.predicate(Z3Utils.SMTLIB_NODE_FUNCTION, mayModelObj.getFormulaVariable()) : Z3Utils.predicate(Z3Utils.SMTLIB_EDGE_FUNCTION, mayModelObj.getFormulaVariable()); Boolean exists = wellFormedModelObjs.get(mayModelObjSmtEncoding); if (exists == null) { exists = state.nextBoolean(); if (mayModelObj instanceof Node) { if (!exists) { // enforce future well-formedness for (Edge edgeAsSrc : ((Node) mayModelObj).getEdgesAsSource()) { wellFormedModelObjs.put( Z3Utils.predicate(Z3Utils.SMTLIB_EDGE_FUNCTION, edgeAsSrc.getFormulaVariable()), new Boolean(false) ); } for (Edge edgeAsTgt : ((Node) mayModelObj).getEdgesAsTarget()) { wellFormedModelObjs.put( Z3Utils.predicate(Z3Utils.SMTLIB_EDGE_FUNCTION, edgeAsTgt.getFormulaVariable()), new Boolean(false) ); } } } else { if (exists) { // enforce future well-formedness wellFormedModelObjs.put( Z3Utils.predicate(Z3Utils.SMTLIB_NODE_FUNCTION, ((Edge) mayModelObj).getSource().getFormulaVariable()), new Boolean(true) ); wellFormedModelObjs.put( Z3Utils.predicate(Z3Utils.SMTLIB_NODE_FUNCTION, ((Edge) mayModelObj).getTarget().getFormulaVariable()), new Boolean(true) ); } } } else { // well-formedness enforced wellFormedModelObjs.remove(mayModelObjSmtEncoding); } smtConcretization += (exists) ? mayModelObjSmtEncoding: Z3Utils.not(mayModelObjSmtEncoding); } smtConcretization = Z3Utils.and(smtConcretization); return smtConcretization; } private void generateRandomSMTLIBConcretizations() throws MMINTException { long maxConcretizations = Math.round(Math.pow(2, mayModelObjs.size())); if (numConcretizations > maxConcretizations) { throw new MMINTException("numConcretizations (" + numConcretizations + ") > maxConcretizations (" + maxConcretizations + ")"); } if (maxConcretizations <= 1) { return; } //TODO MMINT[TOSEM] add heuristics to detect large number of concretizations (when it's more efficient to generate them all and then cut some) smtConcretizations = new HashSet<>(); smtConcretizationsConstraint = ""; for (int i = 0; i < numConcretizations; i++) { String smtConcretization = generateRandomSMTLIBConcretization(); if (smtConcretizations.contains(smtConcretization)) { // duplicate i--; continue; } smtConcretizations.add(smtConcretization); smtConcretizationsConstraint += smtConcretization + '\n'; } smtConcretizationsConstraint = Z3Utils.or(smtConcretizationsConstraint); } private void generateAllsatSMTLIBConcretizations(MAVORoot rootMayModelObj) { smtConcretizations = doMAVOAllsat(rootMayModelObj); smtConcretizationsConstraint = Z3Utils.or(String.join(" ", smtConcretizations)); } private void groundProperty5(Node node) { // property 5: at most one outgoing edge per node // true by construction if (node.getEdgesAsSource().size() <= 1) { smtProperty += Z3Utils.SMTLIB_TRUE; return; } smtProperty += Z3Utils.SMTLIB_OR; // no edges smtProperty += Z3Utils.SMTLIB_AND; for (Edge outEdge : node.getEdgesAsSource()) { smtProperty += Z3Utils.not(Z3Utils.predicate(Z3Utils.SMTLIB_EDGE_FUNCTION, outEdge.getFormulaVariable())); } smtProperty += Z3Utils.SMTLIB_PREDICATE_END; // one edge only for (Edge outEdgeI : node.getEdgesAsSource()) { smtProperty += Z3Utils.SMTLIB_AND + Z3Utils.predicate(Z3Utils.SMTLIB_EDGE_FUNCTION, outEdgeI.getFormulaVariable()); for (Edge outEdgeJ : node.getEdgesAsSource()) { if (outEdgeI == outEdgeJ) { continue; } smtProperty += Z3Utils.not(Z3Utils.predicate(Z3Utils.SMTLIB_EDGE_FUNCTION, outEdgeJ.getFormulaVariable())); } smtProperty += Z3Utils.SMTLIB_PREDICATE_END; } smtProperty += Z3Utils.SMTLIB_PREDICATE_END; } private void groundProperty6(Node node) { // property 6: not more than one edge from a node to another // true by construction if (node.getEdgesAsSource().size() <= 1) { smtProperty += Z3Utils.SMTLIB_TRUE; return; } smtProperty += Z3Utils.SMTLIB_AND; // construct map of target nodes Map<Node, List<Edge>> tgtNode2EdgesMap = new HashMap<Node, List<Edge>>(); for (Edge outEdge : node.getEdgesAsSource()) { List<Edge> tgtNode2Edges = tgtNode2EdgesMap.get(outEdge.getTarget()); if (tgtNode2Edges == null) { tgtNode2Edges = new ArrayList<Edge>(); tgtNode2Edges.add(outEdge); tgtNode2EdgesMap.put(outEdge.getTarget(), tgtNode2Edges); } else { tgtNode2Edges.add(outEdge); } } // use map of target nodes for (List<Edge> tgtNode2Edges : tgtNode2EdgesMap.values()) { // true by construction if (tgtNode2Edges.size() <= 1) { smtProperty += Z3Utils.SMTLIB_TRUE; continue; } smtProperty += Z3Utils.SMTLIB_OR; // no edges smtProperty += Z3Utils.SMTLIB_AND; for (Edge outEdge : tgtNode2Edges) { smtProperty += Z3Utils.not(Z3Utils.predicate(Z3Utils.SMTLIB_EDGE_FUNCTION, outEdge.getFormulaVariable())); } smtProperty += Z3Utils.SMTLIB_PREDICATE_END; // one edge only for (Edge outEdgeI : tgtNode2Edges) { smtProperty += Z3Utils.SMTLIB_AND + Z3Utils.predicate(Z3Utils.SMTLIB_EDGE_FUNCTION, outEdgeI.getFormulaVariable()); for (Edge outEdgeJ : tgtNode2Edges) { if (outEdgeI == outEdgeJ) { continue; } smtProperty += Z3Utils.not(Z3Utils.predicate(Z3Utils.SMTLIB_EDGE_FUNCTION, outEdgeJ.getFormulaVariable())); } smtProperty += Z3Utils.SMTLIB_PREDICATE_END; } smtProperty += Z3Utils.SMTLIB_PREDICATE_END; } smtProperty += Z3Utils.SMTLIB_PREDICATE_END; } private void groundProperty8(Node node) { // property 8: no edges from a node to the same node boolean noSelfLoops = true; for (Edge outEdge : node.getEdgesAsSource()) { // same src and tgt if (outEdge.getTarget() == node) { smtProperty += Z3Utils.not(Z3Utils.predicate(Z3Utils.SMTLIB_EDGE_FUNCTION, outEdge.getFormulaVariable())); noSelfLoops = false; } } // true by construction if (noSelfLoops) { smtProperty += Z3Utils.SMTLIB_TRUE; } } private void groundProperty10(Node node) { // property 10: at least one incoming or outgoing edge per node // false by construction if ((node.getEdgesAsSource().size() + node.getEdgesAsTarget().size()) < 1) { smtProperty += Z3Utils.SMTLIB_FALSE; return; } // at least one edge smtProperty += Z3Utils.SMTLIB_OR; for (Edge outEdge : node.getEdgesAsSource()) { smtProperty += Z3Utils.predicate(Z3Utils.SMTLIB_EDGE_FUNCTION, outEdge.getFormulaVariable()); } for (Edge inEdge : node.getEdgesAsTarget()) { smtProperty += Z3Utils.predicate(Z3Utils.SMTLIB_EDGE_FUNCTION, inEdge.getFormulaVariable()); } smtProperty += Z3Utils.SMTLIB_PREDICATE_END; } private void generateSMTLIBGroundedProperty(Graph graph) { EList<Node> nodes = graph.getNodes(); if (nodes.isEmpty()) { return; } smtProperty = Z3Utils.SMTLIB_AND; for (Node node : nodes) { smtProperty += '\n'; switch (propertyId) { case 5: groundProperty5(node); break; case 6: groundProperty6(node); break; case 8: groundProperty8(node); break; case 10: groundProperty10(node); break; } } smtProperty += '\n' + Z3Utils.SMTLIB_PREDICATE_END; } private MAVOTruthValue doMAVOPropertyCheck() { long startTime = System.nanoTime(); MAVOTruthValue resultMAVO = z3Reasoner.checkMAVOConstraint(smtEncodingAndConcretizations, "", smtProperty, MAVOCheckStrategy.DOUBLE_CHECK); timeMAVO = System.nanoTime() - startTime; return resultMAVO; } private void doClassicalPropertyCheck() { long startTime = System.nanoTime(); Z3IncrementalSolver z3IncSolver = new Z3IncrementalSolver(); Z3Result firstZ3Bool = null; z3IncSolver.firstCheckSatAndGetModel(smtEncoding); for (String smtConcretization : smtConcretizations) { Z3Model z3Model = z3IncSolver.checkSatAndGetModel(Z3Utils.assertion(smtConcretization) + Z3Utils.assertion(smtProperty), Z3IncrementalBehavior.POP); Z3Result z3Bool = z3Model.getZ3Result(); if (firstZ3Bool == null) { // first run only firstZ3Bool = z3Bool; } if (z3Bool == Z3Result.UNKNOWN || z3Bool != firstZ3Bool) { // == result never changes break; } } timeClassical = System.nanoTime() - startTime; } private void doMAVOBackbone() throws MMINTException { long startTime = System.nanoTime(); z3Reasoner.mayBackbone( smtEncodingAndConcretizations + Z3Utils.assertion(smtProperty), z3ModelParser, new HashSet<>(mayModelObjs.values())); timeMAVOBackbone = System.nanoTime() - startTime; } private Set<String> doMAVOAllsat(MAVORoot rootMayModelObj) { long startTime = System.nanoTime(); Set<String> smtConcretizations = z3Reasoner.allSAT( smtEncodingAndConcretizations + Z3Utils.assertion(smtProperty), z3ModelParser, new HashSet<>(mayModelObjs.values()), rootMayModelObj); timeMAVOAllsat = System.nanoTime() - startTime; return smtConcretizations; } @Override public Map<String, Model> run( Map<String, Model> inputsByName, Map<String, GenericElement> genericsByName, Map<String, MID> outputMIDsByName) throws Exception { // input Model mayModel = inputsByName.get(IN_MODEL); MAVORoot rootMayModelObj = this.init(mayModel); // run MAVOTruthValue resultMAVO = doMAVOPropertyCheck(); if (timeClassicalEnabled) { doClassicalPropertyCheck(); speedupClassicalMAVO = ((double) timeClassical) / timeMAVO; } if (resultMAVO == MAVOTruthValue.MAYBE) { if (timeMAVOBackboneEnabled) { doMAVOBackbone(); } if (timeMAVOAllsatEnabled && getPreviousOperator() != null) { doMAVOAllsat(rootMayModelObj); } if (timeMAVOBackboneEnabled && timeMAVOAllsatEnabled) { speedupMAVOAllsatMAVOBackbone = ((double) timeMAVOAllsat) / timeMAVOBackbone; } } // output Properties outputProperties = new Properties(); writeProperties(outputProperties); MIDOperatorIOUtils.writePropertiesFile( outputProperties, this, mayModel, getInputSubdir(), MIDOperatorIOUtils.OUTPUT_PROPERTIES_SUFFIX ); return new HashMap<>(); } }