private static bool testIfValidConfiguration(string configuration , List <NumericOption> participatingOptions, NonBooleanConstraint constraintToTest) { Dictionary <NumericOption, double> numericSelection = new Dictionary <NumericOption, double>(); string[] values = configuration.Split(new char[] { '$' }); for (int i = 0; i < participatingOptions.Count; i++) { numericSelection.Add(participatingOptions.ElementAt(i), Double.Parse(values[i])); } return(constraintToTest.configIsValid(numericSelection)); }
/// <summary> /// The non-boolean constraints are constraints among binary and numeric configuration options. /// Currently, these constraints are implemented as inequation of multiplicative terms. /// However, z3 does not support the multiplication among boolean variables (i.e., binary configuration options) /// and numeric variables (i.e., numeric configuration options). /// Thus, the approach is as follows: /// (1) Create a numeric variable for each binary configuration option in the mixed constraint and add a constraint /// for each of them, so that the numeric variable has the value 1 when the binary configuration option is true; 0 otherwise /// (2) Translate the constraints into z3 /// </summary> /// <param name="constr">The <see cref="NonBooleanConstraint"/> to translate.</param> /// <param name="optionMapping">The mapping of already used binary options and their <see cref="Expr"/> counterparts.</param> /// <param name="context">The z3 <see cref="Context"/> needed for the translatation of these constraints.</param> /// <param name="optionToTerm">A mapping that maps the given option to a term.</param> /// <returns>A <see cref="BoolExpr"/> that represents this constraint.</returns> private static BoolExpr ProcessMixedConstraint(NonBooleanConstraint constr, Dictionary <BinaryOption, Expr> optionMapping, Context context, Dictionary <ConfigurationOption, Expr> optionToTerm) { List <BoolExpr> constraints = new List <BoolExpr>(); // Process the binary options in the formula HashSet <BinaryOption> binOpts = constr.leftHandSide.participatingBoolOptions; foreach (BinaryOption binOpt in constr.rightHandSide.participatingBoolOptions) { if (!binOpts.Contains(binOpt)) { binOpts.Add(binOpt); } } foreach (BinaryOption binOpt in binOpts) { if (!optionMapping.ContainsKey(binOpt)) { Expr newExpr = GenerateDoubleVariable(context, binOpt.Name + "_num"); optionMapping[binOpt] = newExpr; // Add the constraints to ensure that the values of the binary and the numeric variable correspond to each other. BoolExpr constraint = (BoolExpr)optionToTerm[binOpt]; constraints.Add(context.MkImplies(constraint, context.MkEq(newExpr, context.MkFPNumeral(1, context.MkFPSortDouble())))); constraints.Add(context.MkImplies(context.MkNot(constraint), context.MkEq(newExpr, context.MkFPNumeral(0, context.MkFPSortDouble())))); } } // Note: The 'require'-tag is ignored here, as it does not make sense when translating the mixed constraints for z3 // Translate the mixed constraint FPExpr leftSide = (FPExpr)TranslateMixedConstraint(constr.leftHandSide, optionMapping, context, optionToTerm); FPExpr rightSide = (FPExpr)TranslateMixedConstraint(constr.rightHandSide, optionMapping, context, optionToTerm); // Next, check if the constraint has to evaluate to true or not string comparator = constr.comparator; if (constr.GetType() == typeof(MixedConstraint) && ((MixedConstraint)constr).negativeOrPositiveExpr.Equals(MixedConstraint.NEGATIVE)) { comparator = GetNegatedInequation(comparator); } switch (comparator) { case ">=": return(context.MkFPGEq(leftSide, rightSide)); case "<=": return(context.MkFPLEq(leftSide, rightSide)); case "!=": return(context.MkNot(context.MkEq(leftSide, rightSide))); case "=": return(context.MkEq(leftSide, rightSide)); case ">": return(context.MkFPGt(leftSide, rightSide)); case "<": return(context.MkFPLt(leftSide, rightSide)); default: throw new ArgumentException("The inequation sign " + comparator + " is not supported."); } }
private static List <string> transformToBooleanConstraints(NonBooleanConstraint constraint) { List <string> constraints = new List <string>(); List <ConfigurationOption> options = constraint.ParticipatingOptions(); List <List <double> > allValues = options.Select(option => { switch (option) { case BinaryOption _: return(new List <double> { 0, 1 }); case NumericOption numericOption: return(numericOption.getAllValues()); default: throw new NotImplementedException(); } }).ToList(); foreach (List <double> values in CartesianProduct(allValues)) { Dictionary <BinaryOption, BinaryOption.BinaryValue> binarySelection = new Dictionary <BinaryOption, BinaryOption.BinaryValue>(); Dictionary <NumericOption, double> numericSelection = new Dictionary <NumericOption, double>(); for (int i = 0; i < options.Count; i++) { switch (options[i]) { case BinaryOption binaryOption: binarySelection[binaryOption] = values[i] == 0 ? BinaryOption.BinaryValue.Deselected : BinaryOption.BinaryValue.Selected; break; case NumericOption numericOption: numericSelection[numericOption] = values[i]; break; default: throw new NotImplementedException(); } } if (!constraint.configIsValid(new Configuration(binarySelection, numericSelection, false))) { List <string> terms = values.Zip(options, (value, option) => { switch (option) { case BinaryOption _: return(value == 0 ? option.Name : "!" + option.Name); case NumericOption _: return("!" + option.Name + "_" + value); default: throw new NotImplementedException(); } }).ToList(); string newConstraint = String.Join(" | ", terms); constraints.Add(newConstraint); } } return(constraints); }