/** * 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.istar_mavo.operator; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; import org.eclipse.core.runtime.IStatus; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.jdt.annotation.NonNull; import com.microsoft.z3.FuncDecl; import com.microsoft.z3.FuncInterp; import com.microsoft.z3.FuncInterp.Entry; import com.microsoft.z3.Z3Exception; import edu.toronto.cs.se.mmint.MMINTException; import edu.toronto.cs.se.mmint.MIDTypeRegistry; 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.OperatorImpl; import edu.toronto.cs.se.mmint.mid.utils.MIDOperatorIOUtils; import edu.toronto.cs.se.modelepedia.istar_mavo.Actor; import edu.toronto.cs.se.modelepedia.istar_mavo.IStar; import edu.toronto.cs.se.modelepedia.istar_mavo.IStar_MAVOPackage; import edu.toronto.cs.se.modelepedia.istar_mavo.Intention; 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.Z3Model.Z3Result; import edu.toronto.cs.se.modelepedia.z3.Z3Utils; import edu.toronto.cs.se.modelepedia.z3.mavo.Z3MAVOModelParser; public class RE13 extends OperatorImpl { protected enum SMTLIBLabel { fs(IStar_MAVOPackage.eINSTANCE.getIntention_FullySatisfied()), ps(IStar_MAVOPackage.eINSTANCE.getIntention_PartiallySatisfied()), un(IStar_MAVOPackage.eINSTANCE.getIntention_Unknown()), co(IStar_MAVOPackage.eINSTANCE.getIntention_Conflict()), pd(IStar_MAVOPackage.eINSTANCE.getIntention_PartiallyDenied()), fd(IStar_MAVOPackage.eINSTANCE.getIntention_FullyDenied()), no(IStar_MAVOPackage.eINSTANCE.getIntention_NoLabel()); private final EStructuralFeature modelFeature; SMTLIBLabel(EStructuralFeature modelFeature) { this.modelFeature = modelFeature; } EStructuralFeature getModelFeature() { return modelFeature; } } // input-output protected final static @NonNull String IN_MODEL = "istar"; private static final String PROPERTY_IN_TARGETSPROPERTY = "targetsProperty"; private static final String PROPERTY_IN_TARGETSPROPERTY_DEFAULT = ""; private static final String PROPERTY_OUT_TIMEMODEL = "timeModel"; protected static final String PROPERTY_OUT_TIMEANALYSIS = "timeAnalysis"; private static final String PROPERTY_OUT_TIMETARGETS = "timeTargets"; private static final String PROPERTY_OUT_LABELS_SUFFIX = ".labels"; private static final String PROPERTY_OUT_TARGETS = "targets"; // constants protected static final String PREVIOUS_OPERATOR_URI = "http://se.cs.toronto.edu/modelepedia/Operator_IStarMAVOToSMTLIB"; protected static final String SMTLIB_CONCRETIZATION = " c "; private static final Set<String> Z3_MODEL_NODETYPES = new HashSet<String>(); static { Z3_MODEL_NODETYPES.add("Task"); Z3_MODEL_NODETYPES.add("Goal"); Z3_MODEL_NODETYPES.add("SoftGoal"); Z3_MODEL_NODETYPES.add("Resource"); } // input private boolean timeModelEnabled; protected boolean timeTargetsEnabled; private String targetsProperty; // state protected IStar istar; protected Map<String, Intention> intentions; protected Set<String> initialIntentions; protected Z3MAVOModelParser z3ModelParser; protected String smtEncoding; // output private long timeModel; private long timeAnalysis; protected long timeTargets; protected Z3Result targets; @Override public void readInputProperties(Properties inputProperties) throws MMINTException { super.readInputProperties(inputProperties); timeModelEnabled = MIDOperatorIOUtils.getBoolProperty(inputProperties, PROPERTY_OUT_TIMEMODEL+MIDOperatorIOUtils.PROPERTY_IN_OUTPUTENABLED_SUFFIX); timeTargetsEnabled = MIDOperatorIOUtils.getBoolProperty(inputProperties, PROPERTY_OUT_TIMETARGETS+MIDOperatorIOUtils.PROPERTY_IN_OUTPUTENABLED_SUFFIX); targetsProperty = MIDOperatorIOUtils.getOptionalStringProperty(inputProperties, PROPERTY_IN_TARGETSPROPERTY, PROPERTY_IN_TARGETSPROPERTY_DEFAULT); } protected void init() { // state istar = null; intentions = new HashMap<String, Intention>(); initialIntentions = new HashSet<String>(); IStarMAVOToSMTLIB previousOperator = (getPreviousOperator() == null) ? (IStarMAVOToSMTLIB) MIDTypeRegistry.<Operator>getType(PREVIOUS_OPERATOR_URI) : (IStarMAVOToSMTLIB) getPreviousOperator(); z3ModelParser = previousOperator.getZ3MAVOModelParser(); smtEncoding = z3ModelParser.getSMTLIBEncoding(); // output timeModel = -1; timeAnalysis = -1; timeTargets = -1; targets = Z3Result.UNKNOWN; } protected String writeIntentionLabels(Intention intention) { String labels = ""; for (SMTLIBLabel label : SMTLIBLabel.values()) { if ((boolean) intention.eGet(label.getModelFeature())) { labels += label.name() + ","; } } if (!labels.equals("")) { labels = labels.substring(0, labels.length()-1); } return labels; } protected void writeProperties(Properties properties) { properties.setProperty(PROPERTY_OUT_TIMEMODEL, String.valueOf(timeModel)); properties.setProperty(PROPERTY_OUT_TIMEANALYSIS, String.valueOf(timeAnalysis)); properties.setProperty(PROPERTY_OUT_TIMETARGETS, String.valueOf(timeTargets)); properties.setProperty(PROPERTY_OUT_TARGETS, targets.toString()); for (Map.Entry<String, Intention> entry : intentions.entrySet()) { properties.setProperty(entry.getKey()+PROPERTY_OUT_LABELS_SUFFIX, writeIntentionLabels(entry.getValue())); } } private void getConcretizationAnalysisLabel(Map<String, Intention> intentions, Map<String, Set<String>> z3ModelNodes, Set<String> z3LabelNodeIds, SMTLIBLabel label, String nodeType, boolean elseValue) { EStructuralFeature labelFeature = label.getModelFeature(); if (elseValue) { for (Map.Entry<String, Set<String>> z3ModelNode : z3ModelNodes.entrySet()) { String z3NodeId = z3ModelNode.getKey(); if (!z3NodeId.startsWith(nodeType)) { continue; } if (z3LabelNodeIds.contains(z3NodeId)) { continue; } z3ModelNode.getValue().forEach(formulaVar -> intentions.get(formulaVar).eSet(labelFeature, true)); } } else { for (String z3LabelNodeId : z3LabelNodeIds) { Set<String> formulaVars = z3ModelNodes.get(z3LabelNodeId); if (formulaVars == null) { continue; } formulaVars.forEach(formulaVar -> intentions.get(formulaVar).eSet(labelFeature, true)); } } } protected void getConcretizationAnalysisLabels(Map<String, Intention> intentions, Z3Model z3Model) { try { Map<String, Set<String>> z3ModelNodes = z3ModelParser.getZ3MAVOModelNodes(z3Model); com.microsoft.z3.Model z3InternalModel = z3Model.getZ3InternalModel(); for (SMTLIBLabel label : SMTLIBLabel.values()) { for (FuncDecl decl : z3InternalModel.getFuncDecls()) { if (!(decl.getName().toString().equals(label.name()) || decl.getName().toString().contains(label.name()+Z3Utils.Z3_MODEL_SEPARATOR))) { continue; } String nodeType = decl.getDomain()[0].getName().toString(); if (!Z3_MODEL_NODETYPES.contains(nodeType)) { // edge function continue; } FuncInterp interp = z3InternalModel.getFuncInterp(decl); if (interp.getEntries().length == 0) {// function that calls another function continue; } Set<String> z3LabelNodeUniverses = new HashSet<String>(); for (Entry entry : interp.getEntries()) { z3LabelNodeUniverses.add(entry.getArgs()[0].toString()); } getConcretizationAnalysisLabel(intentions, z3ModelNodes, z3LabelNodeUniverses, label, nodeType, Boolean.parseBoolean(interp.getElse().toString())); } } } catch (Z3Exception e) { MMINTException.print(IStatus.WARNING, "Can't optimize analysis, skipping it", e); } } protected void doAnalysis(Z3IncrementalSolver z3IncSolver) { long startTime = System.nanoTime(); z3IncSolver.firstCheckSatAndGetModel(smtEncoding); if (timeModelEnabled) { timeModel = System.nanoTime() - startTime; } String intentionProperty, labelProperty; for (Map.Entry<String, Intention> entry : intentions.entrySet()) { String intentionFormulaVar = entry.getKey(); Intention intention = entry.getValue(); if (initialIntentions.contains(intentionFormulaVar)) { // skip intentions with initial label continue; } intentionProperty = Z3Utils.SMTLIB_ASSERT; if (intention.isMay()) { intentionProperty += Z3Utils.SMTLIB_AND + Z3Utils.exists( Z3Utils.emptyPredicate(SMTLIB_CONCRETIZATION + intention.eClass().getName()), Z3Utils.predicate(Z3Utils.SMTLIB_NODE_FUNCTION, intentionFormulaVar + SMTLIB_CONCRETIZATION) ) ; } intentionProperty += Z3Utils.SMTLIB_FORALL + Z3Utils.SMTLIB_PREDICATE_START + Z3Utils.emptyPredicate(SMTLIB_CONCRETIZATION + intention.eClass().getName()) + Z3Utils.SMTLIB_PREDICATE_END + Z3Utils.SMTLIB_IMPLICATION + Z3Utils.predicate(Z3Utils.SMTLIB_NODE_FUNCTION, intentionFormulaVar + SMTLIB_CONCRETIZATION) ; for (SMTLIBLabel label : SMTLIBLabel.values()) { if ((boolean) intention.eGet(label.getModelFeature())) { // skip already checked continue; } labelProperty = intentionProperty + Z3Utils.predicate(Z3Utils.SMTLIB_PREDICATE_START + label.name(), SMTLIB_CONCRETIZATION) + Z3Utils.SMTLIB_PREDICATE_END + Z3Utils.SMTLIB_PREDICATE_END; if (intention.isMay()) { labelProperty += Z3Utils.SMTLIB_PREDICATE_END; } labelProperty += Z3Utils.SMTLIB_PREDICATE_END; Z3Model z3Model = z3IncSolver.checkSatAndGetModel(labelProperty, Z3IncrementalBehavior.POP); if (z3Model.getZ3Result() == Z3Result.SAT) { intention.eSet(label.getModelFeature(), true); getConcretizationAnalysisLabels(intentions, z3Model); } } } timeAnalysis = System.nanoTime() - startTime; } protected Z3Model doTargets(Z3IncrementalSolver z3IncSolver) { long startTime = System.nanoTime(); Z3Model z3Model = z3IncSolver.checkSatAndGetModel(Z3Utils.assertion(targetsProperty), Z3IncrementalBehavior.NORMAL); targets = z3Model.getZ3Result(); timeTargets = System.nanoTime() - startTime; return z3Model; } protected Map<String, Intention> collectIntentions(IStar istar) { Map<String, Intention> intentions = new HashMap<String, Intention>(); for (Actor actor : istar.getActors()) { for (Intention intention : actor.getIntentions()) { intentions.put(intention.getFormulaVariable(), intention); } } for (Intention intention : istar.getDependums()) { intentions.put(intention.getFormulaVariable(), intention); } return intentions; } protected void collectAnalysisModelObjects(Model istarModel) throws MMINTException { istar = (IStar) istarModel.getEMFInstanceRoot(); intentions = collectIntentions(istar); for (Map.Entry<String, Intention> entry : intentions.entrySet()) { Intention intention = entry.getValue(); if ( intention.isFullySatisfied() || intention.isPartiallySatisfied() || intention.isUnknown() || intention.isConflict() || intention.isPartiallyDenied() || intention.isFullyDenied() || intention.isNoLabel() ) { initialIntentions.add(entry.getKey()); } } } @Override public Map<String, Model> run( Map<String, Model> inputsByName, Map<String, GenericElement> genericsByName, Map<String, MID> outputMIDsByName) throws Exception { // input Model istarModel = inputsByName.get(IN_MODEL); this.init(); // run solver collectAnalysisModelObjects(istarModel); Z3IncrementalSolver z3IncSolver = new Z3IncrementalSolver(); doAnalysis(z3IncSolver); if (timeTargetsEnabled) { doTargets(z3IncSolver); } // output Properties outputProperties = new Properties(); writeProperties(outputProperties); MIDOperatorIOUtils.writePropertiesFile( outputProperties, this, istarModel, null, MIDOperatorIOUtils.OUTPUT_PROPERTIES_SUFFIX ); return new HashMap<>(); } }