From 5dc344cfcd5ce7fa1465570b5947e0d21321340d Mon Sep 17 00:00:00 2001 From: Andreas Bayha <bayha@fortiss.org> Date: Fri, 21 Apr 2023 18:13:58 +0200 Subject: [PATCH] Variability: Added extraction of counterexample to ProductLineAnalysis The analysis has been refined with meaningful violations as a result. Those include the violating objects, the constraints and (once implemented) the violating feature configuration. Issue-ref: 4240 Issue-URL: af3#4240 Signed-off-by: Andreas Bayha <bayha@fortiss.org> --- .../analysis/IProductLineConstraint.java | 50 +++++++ .../analysis/ProductLineBaseAnalysis.java | 129 +++++++++++++++--- .../analysis/ProductLineBaseTranslation.java | 59 ++++---- .../ProductLineConstraintViolation.java | 66 +++++++++ 4 files changed, 257 insertions(+), 47 deletions(-) create mode 100644 org.fortiss.variability/src/org/fortiss/variability/analysis/IProductLineConstraint.java create mode 100644 org.fortiss.variability/src/org/fortiss/variability/analysis/ProductLineConstraintViolation.java diff --git a/org.fortiss.variability/src/org/fortiss/variability/analysis/IProductLineConstraint.java b/org.fortiss.variability/src/org/fortiss/variability/analysis/IProductLineConstraint.java new file mode 100644 index 000000000..47d009b35 --- /dev/null +++ b/org.fortiss.variability/src/org/fortiss/variability/analysis/IProductLineConstraint.java @@ -0,0 +1,50 @@ +/*-------------------------------------------------------------------------+ +| Copyright 2023 fortiss GmbH | +| | +| Licensed under the Apache License, Version 2.0 (the "License"); | +| you may not use this file except in compliance with the License. | +| You may obtain a copy of the License at | +| | +| http://www.apache.org/licenses/LICENSE-2.0 | +| | +| Unless required by applicable law or agreed to in writing, software | +| distributed under the License is distributed on an "AS IS" BASIS, | +| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | +| See the License for the specific language governing permissions and | +| limitations under the License. | ++--------------------------------------------------------------------------*/ +package org.fortiss.variability.analysis; + +import java.util.List; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.fortiss.variability.model.features.configuration.VariantConfiguration; + +import com.microsoft.z3.BoolExpr; +import com.microsoft.z3.Expr; + +/** + * + * @author bayha + */ +public interface IProductLineConstraint { + + public String getConstraintName(); + + public String getConstraintMessage(List<EObject> violatingObjects, + VariantConfiguration violatingConfiguration); + + public Expr[] getQuantifierVariables(); + + public EClass[] getVariablesClasses(); + + public BoolExpr[] getContainmentExpr(); + + public BoolExpr getBody(); + + public boolean isForAll(); + + public List<Expr> getVariableTracker(); + +} diff --git a/org.fortiss.variability/src/org/fortiss/variability/analysis/ProductLineBaseAnalysis.java b/org.fortiss.variability/src/org/fortiss/variability/analysis/ProductLineBaseAnalysis.java index ee3aaba5a..ef9785f44 100644 --- a/org.fortiss.variability/src/org/fortiss/variability/analysis/ProductLineBaseAnalysis.java +++ b/org.fortiss.variability/src/org/fortiss/variability/analysis/ProductLineBaseAnalysis.java @@ -1,7 +1,6 @@ package org.fortiss.variability.analysis; import static com.microsoft.z3.Status.UNSATISFIABLE; -import static java.util.Arrays.deepToString; import static org.fortiss.tooling.kernel.utils.LoggingUtils.error; import static org.fortiss.variability.VariabilityActivator.getDefault; @@ -35,7 +34,14 @@ public class ProductLineBaseAnalysis { protected final ProductLineBaseTranslation translation; /** List of all translated correctness constraints. */ - protected List<BoolExpr> constraints = new ArrayList<BoolExpr>(); + protected List<BoolExpr> rawConstraints = new ArrayList<BoolExpr>(); + + protected List<BoolExpr> constraintTrackers = new ArrayList<BoolExpr>(); + + protected List<IProductLineConstraint> plConstraints = new ArrayList<IProductLineConstraint>(); + + protected Map<Expr, IProductLineConstraint> constraintTracker2Constraint = + new HashMap<Expr, IProductLineConstraint>(); protected final EObject model; @@ -57,13 +63,17 @@ public class ProductLineBaseAnalysis { * @return 'true' if all constraints are fulfilled for all variants of the product-line * analysis. 'false' otherwise. */ - public boolean doCheck() { + public final List<ProductLineConstraintViolation> doCheck() { final long start = System.currentTimeMillis(); translation.doTranslate(model); + addConstriants(); + translateConstraints(); + addRawConstraints(); + Solver solver = getSolver(); String modelName = ((INamedElement)model).getName(); @@ -88,30 +98,59 @@ public class ProductLineBaseAnalysis { System.out.println("Runtime " + modelName + ": " + (end - start) + " total, " + (startSmt - start) + " translation, " + (end - startSmt) + " SMT"); + List<ProductLineConstraintViolation> ret = new ArrayList<ProductLineConstraintViolation>(); + if(result.equals(UNSATISFIABLE)) { - BoolExpr[] unsatCore = solver.getUnsatCore(); + // No constraint was violated + + // BoolExpr[] unsatCore = solver.getUnsatCore(); + // + // PrintWriter printWriter; + // + // try { + // printWriter = new PrintWriter( + // "C:\\Users\\bayha\\tmp\\z3" + modelName + (start % 159739431) + "out"); + // + // for(BoolExpr b : unsatCore) { + // BoolExpr coreExpr = tracToExpr.get(b); + // + // printWriter.println(coreExpr); + // } + // + // printWriter.close(); + // } catch(FileNotFoundException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } + // + // System.out.println(deepToString(unsatCore)); + } else { + + Model z3Model = solver.getModel(); - PrintWriter printWriter; + for(BoolExpr constTrack : constraintTrackers) { + Expr constInterp = z3Model.getConstInterp(constTrack); - try { - printWriter = new PrintWriter( - "C:\\Users\\bayha\\tmp\\z3" + modelName + (start % 159739431) + "out"); + if(constInterp instanceof BoolExpr) { + if(constInterp.equals(ctx.mkTrue())) { + IProductLineConstraint failedConstraint = + constraintTracker2Constraint.get(constTrack); + + List<EObject> violatingObjects = new ArrayList<EObject>(); + for(Expr varTrack : failedConstraint.getVariableTracker()) { + Expr interpr = z3Model.getConstInterp(varTrack); + EObject eObject = translation.getExpr2EObject().get(interpr); - for(BoolExpr b : unsatCore) { - BoolExpr coreExpr = tracToExpr.get(b); + violatingObjects.add(eObject); + } - printWriter.println(coreExpr); + ret.add(new ProductLineConstraintViolation(failedConstraint, + violatingObjects, null)); + } } - printWriter.close(); - } catch(FileNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); } - System.out.println(deepToString(unsatCore)); - } else { - Model z3Model = solver.getModel(); try { PrintWriter printWriter = new PrintWriter( "C:\\Users\\bayha\\tmp\\z3" + modelName + (start % 159739431) + "out"); @@ -125,7 +164,7 @@ public class ProductLineBaseAnalysis { translation.ctx.close(); - return result.equals(UNSATISFIABLE); + return ret; } /** Mapping from tracking variables to z3 expression (used for counterexample extraction). */ @@ -135,17 +174,45 @@ public class ProductLineBaseAnalysis { protected Solver getSolver() { Solver solver = translation.createSolver(); - if(constraints.size() > 0) { - solver.add(translation.ctx.mkOr(constraints.toArray(new BoolExpr[constraints.size()]))); + if(rawConstraints.size() > 0) { + solver.add(rawConstraints.toArray(new BoolExpr[rawConstraints.size()])); + + solver.add(translation.ctx + .mkOr(constraintTrackers.toArray(new BoolExpr[constraintTrackers.size()]))); } return solver; } /** Needs to be overwritten to add the constraints to be checked. */ - protected void translateConstraints() { + protected void addRawConstraints() { // EMPTY } + protected void addConstriants() { + // EMPTY + } + + protected void translateConstraints() { + for(IProductLineConstraint constraint : plConstraints) { + Expr[] quantifierValiables = constraint.getQuantifierVariables(); + EClass[] variableClasses = constraint.getVariablesClasses(); + BoolExpr[] containmentExprs = constraint.getContainmentExpr(); + BoolExpr body = constraint.getBody(); + + boolean isForAll = constraint.isForAll(); + + BoolExpr quantifier = createLiftedConstraint(quantifierValiables, variableClasses, + containmentExprs, body, isForAll); + + BoolExpr constraintTracker = + ctx.mkBoolConst("CONSTRAINT_" + constraint.getConstraintName()); + constraintTracker2Constraint.put(constraintTracker, constraint); + + rawConstraints.add(ctx.mkEq(constraintTracker, quantifier)); + constraintTrackers.add(constraintTracker); + } + } + /** Automatically lifts the given constraint */ protected BoolExpr liftConstraint(Expr[] quantifierValiables, EClass[] variableClasses, BoolExpr body, boolean isForAll) { @@ -153,6 +220,24 @@ public class ProductLineBaseAnalysis { isForAll); } + // protected BoolExpr createLiftedQuantifier(String[] quantifierValiableNames, + // EClass[] variableClasses, BoolExpr[] containmentExprs, BoolExpr body, + // boolean isForAll) { + // + // int numVar = quantifierValiableNames.length; + // Expr[] quantifierValiables = new Expr[numVar]; + // + // for(int i = 0; i < numVar; i++) { + // EnumSort sort = translation.geteClass2Datatype().get(variableClasses[i]); + // Expr expr = ctx.mkConst(quantifierValiableNames[i], sort); + // + // quantifierValiables[i] = expr; + // } + // + // return createLiftedConstraint(quantifierValiables, variableClasses, containmentExprs, body, + // isForAll); + // } + /** Automatically lifts the given constraint */ protected BoolExpr createLiftedConstraint(Expr[] quantifierValiables, EClass[] variableClasses, BoolExpr[] containmentExprs, BoolExpr body, boolean isForAll) { diff --git a/org.fortiss.variability/src/org/fortiss/variability/analysis/ProductLineBaseTranslation.java b/org.fortiss.variability/src/org/fortiss/variability/analysis/ProductLineBaseTranslation.java index c3a71c219..56476d967 100644 --- a/org.fortiss.variability/src/org/fortiss/variability/analysis/ProductLineBaseTranslation.java +++ b/org.fortiss.variability/src/org/fortiss/variability/analysis/ProductLineBaseTranslation.java @@ -59,11 +59,11 @@ import com.microsoft.z3.Symbol; public class ProductLineBaseTranslation { /** Z3 sort for base type integer. */ - protected static IntSort INT_SORT; + protected IntSort intSort; /** Z3 sort for base type string. */ - protected static SeqSort STRING_SORT; + protected SeqSort stringSort; /** Z3 sort for base type boolean. */ - protected static BoolSort BOOL_SORT; + protected BoolSort boolSort; /** Mapping from classes to all their objects */ protected BucketSetMap<EClass, EObject> allObjects = new BucketSetMap<EClass, EObject>(); @@ -105,6 +105,9 @@ public class ProductLineBaseTranslation { */ protected DualKeyMap<EObject, EClass, Expr> eObject2Expr = new DualKeyMap<EObject, EClass, Expr>(); + + protected Map<Expr, EObject> expr2EObject = new HashMap<Expr, EObject>(); + /** Mapping of enumeration literals to the translated Z3 expression. */ protected Map<Enum<?>, Expr> eLiteral2Expr = new HashMap<Enum<?>, Expr>(); /** Mapping of classes to their respective z3 expression for 'null' */ @@ -124,7 +127,7 @@ public class ProductLineBaseTranslation { protected Context ctx = new Context(); /** List of all translated assertions for the model. */ - protected List<BoolExpr> assertions = new ArrayList<BoolExpr>(); + protected List<BoolExpr> modelAssertions = new ArrayList<BoolExpr>(); /** List of all assertions for the feature model. */ protected List<BoolExpr> featureModelAssertions = new ArrayList<BoolExpr>(); /** List of all assertions which encode the presence conditions. */ @@ -142,27 +145,32 @@ public class ProductLineBaseTranslation { return eClass2Datatype; } - // /** Returns eReference2FunDecl. */ - // public DualKeyMap<EReference, EClass, FuncDecl> geteReference2FunDecl() { - // return eReference2FunDecl; - // } - /** Returns attRefNames2FuncDecl. */ public DualKeyMap<EClass, String, FuncDecl> getAttRefNames2FuncDecl() { return attRefNames2FuncDecl; } + /** Returns boolSort. */ + public BoolSort getBoolSort() { + return boolSort; + } + + /** Returns expr2EObject. */ + public Map<Expr, EObject> getExpr2EObject() { + return expr2EObject; + } + /** * Initialization of analysis and base types. */ private void initialize() { - INT_SORT = ctx.mkIntSort(); - STRING_SORT = ctx.mkStringSort(); - BOOL_SORT = ctx.mkBoolSort(); + intSort = ctx.mkIntSort(); + stringSort = ctx.mkStringSort(); + boolSort = ctx.mkBoolSort(); - eDataType2Sort.put(EcorePackage.Literals.EINT, INT_SORT); - eDataType2Sort.put(EcorePackage.Literals.ESTRING, STRING_SORT); - eDataType2Sort.put(EcorePackage.Literals.EBOOLEAN, BOOL_SORT); + eDataType2Sort.put(EcorePackage.Literals.EINT, intSort); + eDataType2Sort.put(EcorePackage.Literals.ESTRING, stringSort); + eDataType2Sort.put(EcorePackage.Literals.EBOOLEAN, boolSort); } /** Traverses the model to collect all classes and objects to be translated. */ @@ -215,7 +223,7 @@ public class ProductLineBaseTranslation { protected void translatePresenceConditions() { for(EClass ec : allObjects.keySet()) { EnumSort ecSort = eClass2Datatype.get(ec); - FuncDecl selFun = ctx.mkFuncDecl("SELECTED_" + ec.getName(), ecSort, BOOL_SORT); + FuncDecl selFun = ctx.mkFuncDecl("SELECTED_" + ec.getName(), ecSort, boolSort); eClass2SelectionFunction.put(ec, selFun); for(EObject eo : allObjects.get(ec)) { @@ -380,13 +388,13 @@ public class ProductLineBaseTranslation { solver.add(presenceConditionAssertions .toArray(new BoolExpr[presenceConditionAssertions.size()])); // solver.add(assertions.toArray(new BoolExpr[assertions.size()])); - BoolExpr[] assertionsArray = assertions.toArray(new BoolExpr[assertions.size()]); - List<BoolExpr> tracList = assertions.stream() + BoolExpr[] assertionsArray = modelAssertions.toArray(new BoolExpr[modelAssertions.size()]); + List<BoolExpr> tracList = modelAssertions.stream() .map(a -> ctx.mkBoolConst("tracVar" + a.getId())).collect(toList()); - BoolExpr[] assertionsTracArray = tracList.toArray(new BoolExpr[assertions.size()]); + BoolExpr[] assertionsTracArray = tracList.toArray(new BoolExpr[modelAssertions.size()]); - for(int i = 0; i < assertions.size(); i++) { - tracToExpr.put(tracList.get(i), assertions.get(i)); + for(int i = 0; i < modelAssertions.size(); i++) { + tracToExpr.put(tracList.get(i), modelAssertions.get(i)); } solver.assertAndTrack(assertionsArray, assertionsTracArray); @@ -409,7 +417,7 @@ public class ProductLineBaseTranslation { Expr attVal = translateAttributeValue(eo, ea); BoolExpr attValAssertion = ctx.mkEq(ctx.mkApp(attFunc, eObject2Expr.get(eo, ec)), attVal); - assertions.add(attValAssertion); + modelAssertions.add(attValAssertion); } } } @@ -422,17 +430,17 @@ public class ProductLineBaseTranslation { Expr eoExpr = eObject2Expr.get(eo, ec); Expr refVal = translateReferenceValue(eo, er); BoolExpr rafValAssertion = ctx.mkEq(ctx.mkApp(refFunc, eoExpr), refVal); - assertions.add(rafValAssertion); + modelAssertions.add(rafValAssertion); } // Null must always only point to null.... EClass refType = er.getEReferenceType(); Expr nullElement = eClass2NullElement.get(ec); if(er.getUpperBound() > 0) { - assertions.add(ctx.mkEq(ctx.mkApp(refFunc, nullElement), + modelAssertions.add(ctx.mkEq(ctx.mkApp(refFunc, nullElement), eClass2NullElement.get(refType))); } else { - assertions.add(ctx.mkEq(ctx.mkApp(refFunc, nullElement), + modelAssertions.add(ctx.mkEq(ctx.mkApp(refFunc, nullElement), ctx.mkEmptySeq(eReference2Sort.get(er)))); } } @@ -741,6 +749,7 @@ public class ProductLineBaseTranslation { Expr[] consts = enumSort.getConsts(); for(int i = 0; i < objs.size(); i++) { eObject2Expr.put(objs.get(i), cls, consts[i]); + expr2EObject.put(consts[i], objs.get(i)); } Expr noneExpr = consts[consts.length - 1]; eClass2NullElement.put(cls, noneExpr); diff --git a/org.fortiss.variability/src/org/fortiss/variability/analysis/ProductLineConstraintViolation.java b/org.fortiss.variability/src/org/fortiss/variability/analysis/ProductLineConstraintViolation.java new file mode 100644 index 000000000..7d4de79b3 --- /dev/null +++ b/org.fortiss.variability/src/org/fortiss/variability/analysis/ProductLineConstraintViolation.java @@ -0,0 +1,66 @@ +/*-------------------------------------------------------------------------+ +| Copyright 2023 fortiss GmbH | +| | +| Licensed under the Apache License, Version 2.0 (the "License"); | +| you may not use this file except in compliance with the License. | +| You may obtain a copy of the License at | +| | +| http://www.apache.org/licenses/LICENSE-2.0 | +| | +| Unless required by applicable law or agreed to in writing, software | +| distributed under the License is distributed on an "AS IS" BASIS, | +| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | +| See the License for the specific language governing permissions and | +| limitations under the License. | ++--------------------------------------------------------------------------*/ +package org.fortiss.variability.analysis; + +import java.util.List; + +import org.eclipse.emf.ecore.EObject; +import org.fortiss.variability.model.features.configuration.VariantConfiguration; + +/** + * + * @author bayha + */ +public class ProductLineConstraintViolation { + + protected List<EObject> violatingElements; + + protected IProductLineConstraint constraint; + + private VariantConfiguration violatingConfiguration; + + /** + * @param constraint + * @param violatingElements + * @param violatingConfiguration + */ + public ProductLineConstraintViolation(IProductLineConstraint constraint, + List<EObject> violatingElements, VariantConfiguration violatingConfiguration) { + this.constraint = constraint; + this.violatingElements = violatingElements; + this.violatingConfiguration = violatingConfiguration; + } + + /** Returns violatingElements. */ + public List<EObject> getViolatingElements() { + return violatingElements; + } + + /** Returns errorMessage. */ + public String getErrorMessage() { + return constraint.getConstraintMessage(violatingElements, violatingConfiguration); + } + + /** Returns constraint. */ + public IProductLineConstraint getConstraint() { + return constraint; + } + + /** Returns violatingConfiguration. */ + public VariantConfiguration getViolatingConfiguration() { + return violatingConfiguration; + } +} -- GitLab