/// <summary> /// The method aims at finding a configuration which is similar to the given configuration, but does not contain the optionToBeRemoved. If further options need to be removed from the given configuration, they are outputed in removedElements. /// </summary> /// <param name="optionToBeRemoved">The binary configuration option that must not be part of the new configuration.</param> /// <param name="originalConfig">The configuration for which we want to find a similar one.</param> /// <param name="removedElements">If further options need to be removed from the given configuration to build a valid configuration, they are outputed in this list.</param> /// <param name="vm">The variability model containing all options and their constraints.</param> /// <returns>A configuration that is valid, similar to the original configuration and does not contain the optionToBeRemoved.</returns> public List <BinaryOption> GenerateConfigWithoutOption(BinaryOption optionToBeRemoved, List <BinaryOption> originalConfig, out List <BinaryOption> removedElements, VariabilityModel vm) { removedElements = new List <BinaryOption>(); var originalConfigWithoutRemoved = originalConfig.Where(x => !x.Equals(optionToBeRemoved)); List <BoolExpr> variables; Dictionary <BoolExpr, BinaryOption> termToOption; Dictionary <BinaryOption, BoolExpr> optionToTerm; Tuple <Context, BoolExpr> z3Tuple = Z3Solver.GetInitializedBooleanSolverSystem(out variables, out optionToTerm, out termToOption, vm, this.henard); Context z3Context = z3Tuple.Item1; BoolExpr z3Constraints = z3Tuple.Item2; List <BoolExpr> constraints = new List <BoolExpr>(); constraints.Add(z3Constraints); constraints.Add(z3Context.MkNot(optionToTerm[optionToBeRemoved])); ArithExpr[] minGoals = new ArithExpr[variables.Count]; for (int r = 0; r < variables.Count; r++) { BinaryOption currOption = termToOption[variables[r]]; ArithExpr numericVariable = z3Context.MkIntConst(currOption.Name); int weight = -1000; if (!originalConfigWithoutRemoved.Contains(currOption)) { weight = 1000; } else if (currOption.Equals(optionToBeRemoved)) { weight = 100000; } constraints.Add(z3Context.MkEq(numericVariable, z3Context.MkITE(variables[r], z3Context.MkInt(weight), z3Context.MkInt(0)))); minGoals[r] = numericVariable; } Optimize optimizer = z3Context.MkOptimize(); optimizer.Assert(constraints.ToArray()); optimizer.MkMinimize(z3Context.MkAdd(minGoals)); if (optimizer.Check() != Status.SATISFIABLE) { return(null); } else { Model model = optimizer.Model; List <BinaryOption> similarConfig = RetrieveConfiguration(variables, model, termToOption); removedElements = originalConfigWithoutRemoved.Except(similarConfig).ToList(); return(similarConfig); } }
/// <summary> /// Based on a given (partial) configuration and a variability, we aim at finding all optimally maximal or minimal (in terms of selected binary options) configurations. /// </summary> /// <param name="config">The (partial) configuration which needs to be expanded to be valid.</param> /// <param name="vm">Variability model containing all options and their constraints.</param> /// <param name="minimize">If true, we search for the smallest (in terms of selected options) valid configuration. If false, we search for the largest one.</param> /// <param name="unwantedOptions">Binary options that we do not want to become part of the configuration. Might be part if there is no other valid configuration without them</param> /// <returns>A list of configurations that satisfies the VM and the goal (or null if there is none).</returns> public List <List <BinaryOption> > MaximizeConfig(List <BinaryOption> config, VariabilityModel vm, bool minimize, List <BinaryOption> unwantedOptions) { List <BoolExpr> variables; Dictionary <BoolExpr, BinaryOption> termToOption; Dictionary <BinaryOption, BoolExpr> optionToTerm; Tuple <Context, BoolExpr> z3Tuple = Z3Solver.GetInitializedBooleanSolverSystem(out variables, out optionToTerm, out termToOption, vm, this.henard); Context z3Context = z3Tuple.Item1; BoolExpr z3Constraints = z3Tuple.Item2; List <BoolExpr> constraints = new List <BoolExpr>(); constraints.Add(z3Constraints); List <BoolExpr> requireConfigs = new List <BoolExpr>(); if (config != null) { foreach (BinaryOption option in config) { requireConfigs.Add(optionToTerm[option]); } constraints.Add(z3Context.MkAnd(requireConfigs.ToArray())); } ArithExpr[] optimizationGoals = new ArithExpr[variables.Count]; for (int r = 0; r < variables.Count; r++) { BinaryOption currOption = termToOption[variables[r]]; ArithExpr numericVariable = z3Context.MkIntConst(currOption.Name); constraints.Add(z3Context.MkEq(numericVariable, z3Context.MkITE(variables[r], z3Context.MkInt(1), z3Context.MkInt(0)))); optimizationGoals[r] = numericVariable; } Optimize optimizer = z3Context.MkOptimize(); optimizer.Assert(constraints.ToArray()); optimizer.MkMaximize(z3Context.MkAdd(optimizationGoals)); if (optimizer.Check() != Status.SATISFIABLE) { return(null); } List <BinaryOption> solution = RetrieveConfiguration(variables, optimizer.Model, termToOption); return(new List <List <BinaryOption> > { solution }); }
/// <summary> /// Creates a sample of configurations, by iteratively adding a configuration that has the maximal manhattan distance /// to the configurations that were previously selected. /// </summary> /// <param name="vm">The domain for sampling.</param> /// <param name="minimalConfiguration">A minimal configuration that will be used as starting point.</param> /// <param name="numberToSample">The number of configurations that should be sampled.</param> /// <param name="optionWeight">Weight assigned to optional binary options.</param> /// <returns>A list of distance maximized configurations.</returns> public List <List <BinaryOption> > DistanceMaximization(VariabilityModel vm, List <BinaryOption> minimalConfiguration, int numberToSample, int optionWeight) { List <BoolExpr> variables; Dictionary <BoolExpr, BinaryOption> termToOption; Dictionary <BinaryOption, BoolExpr> optionToTerm; Tuple <Context, BoolExpr> z3Tuple = Z3Solver.GetInitializedBooleanSolverSystem(out variables, out optionToTerm, out termToOption, vm, this.henard); Context z3Context = z3Tuple.Item1; BoolExpr z3Constraints = z3Tuple.Item2; List <BoolExpr> constraints = new List <BoolExpr>(); constraints.Add(z3Constraints); List <List <BinaryOption> > sample = new List <List <BinaryOption> >(); sample.Add(minimalConfiguration); Dictionary <BinaryOption, ArithExpr> optionToInt = generateIntConstants(z3Context, constraints, variables, termToOption); while (numberToSample > sample.Count) { List <ArithExpr> goals = generateDistMaximizationGoals(sample, optionToInt, z3Context, vm, optionWeight); Optimize optimizer = z3Context.MkOptimize(); optimizer.Assert(constraints); optimizer.MkMaximize(z3Context.MkAdd(goals)); if (optimizer.Check() != Status.SATISFIABLE) { GlobalState.logInfo.logLine("No more solutions available."); return(sample); } else { Model model = optimizer.Model; List <BinaryOption> maxDist = RetrieveConfiguration(variables, model, termToOption); sample.Add(maxDist); constraints.Add(z3Context.MkNot(Z3Solver.ConvertConfiguration(z3Context, maxDist, optionToTerm, vm))); } } return(sample); }
/// <summary> /// Solves the clauses and returns a solution. /// </summary> /// <param name="status">The solver status.</param> /// <returns>The solution.</returns> public Dictionary <string, bool> Solve(out SolverStatus status) { // Use Z3 and figure out the variable assignments using (Context context = new Context()) { Dictionary <string, Z3Variable> variables = GetVariables(context, this.hard_clauses.Union(this.soft_clauses.Keys)); List <BoolExpr> hard_clauses = GenerateClauses(context, this.hard_clauses, variables); Dictionary <BoolExpr, uint> soft_clauses = GenerateClauses(context, this.soft_clauses, variables); Optimize optimize = context.MkOptimize(); optimize.Assert(hard_clauses); foreach (BoolExpr clause in soft_clauses.Keys) { optimize.AssertSoft(clause, soft_clauses[clause], "group"); } Status solver_status = optimize.Check(); if (solver_status == Status.SATISFIABLE) { Dictionary <string, bool> assignments = new Dictionary <string, bool>(); foreach (string variable in variables.Keys) { Expr expr = optimize.Model.Evaluate(variables[variable].Positive); if (expr.BoolValue != Z3_lbool.Z3_L_UNDEF) { assignments.Add(variable, expr.BoolValue == Z3_lbool.Z3_L_TRUE ? true : false); } } status = SolverStatus.Satisfiable; return(assignments); } status = SolverStatus.Unsatisfiable; return(null); } }
internal void Solve(Z3BaseParams parameters, IEnumerable <IGoal> modelGoals, Action <int> addRow, Func <int, ArithExpr> mkGoalRow, Action <Z3Result> setResult) { _variables.Clear(); _goals.Clear(); try { // Mark that we are in solving phase _isSolving = true; // Construct Z3 ConstructSolver(parameters); // Add all the variables foreach (int vid in _model.VariableIndices) { AddVariable(vid); } // Add all the rows foreach (int rid in _model.RowIndices) { addRow(rid); } // Add enabled goals to optimization problem foreach (IGoal g in modelGoals) { if (!g.Enabled) { continue; } ArithExpr gr = mkGoalRow(g.Index); if (g.Minimize) { _goals.Add(g, _optSolver.MkMinimize(gr)); } else { _goals.Add(g, _optSolver.MkMaximize(gr)); } } if (_goals.Any() && parameters.SMT2LogFile != null) { Debug.WriteLine("Dumping SMT2 benchmark to log file..."); File.WriteAllText(parameters.SMT2LogFile, _optSolver.ToString()); } bool aborted = parameters.QueryAbort(); if (!aborted) { // Start the abort thread AbortWorker abortWorker = new AbortWorker(_context, parameters.QueryAbort); Thread abortThread = new Thread(abortWorker.Start); abortThread.Start(); // Now solve the problem Status status = _optSolver.Check(); // Stop the abort thread abortWorker.Stop(); abortThread.Join(); switch (status) { case Status.SATISFIABLE: Microsoft.Z3.Model model = _optSolver.Model; Debug.Assert(model != null, "Should be able to get Z3 model."); // Remember the solution values foreach (KeyValuePair <int, Expr> pair in _variables) { var value = Utils.ToRational(model.Eval(pair.Value, true)); _model.SetValue(pair.Key, value); } // Remember all objective values foreach (var pair in _goals) { var optimalValue = Utils.ToRational(pair.Value.Upper); _model.SetValue(pair.Key.Index, optimalValue); } model.Dispose(); setResult(_goals.Any() ? Z3Result.Optimal : Z3Result.Feasible); break; case Status.UNSATISFIABLE: setResult(Z3Result.Infeasible); break; case Status.UNKNOWN: if (abortWorker.Aborted) { Microsoft.Z3.Model subOptimalModel = _optSolver.Model; if (subOptimalModel != null && subOptimalModel.NumConsts != 0) { // Remember the solution values foreach (KeyValuePair <int, Expr> pair in _variables) { var value = Utils.ToRational(subOptimalModel.Eval(pair.Value, true)); _model.SetValue(pair.Key, value); } // Remember all objective values foreach (var pair in _goals) { var optimalValue = Utils.ToRational(pair.Value.Upper); _model.SetValue(pair.Key.Index, optimalValue); } subOptimalModel.Dispose(); setResult(Z3Result.LocalOptimal); } else { setResult(Z3Result.Infeasible); } } else { setResult(Z3Result.Interrupted); } break; default: Debug.Assert(false, "Unrecognized Z3 Status"); break; } } } finally { _isSolving = false; } // Now kill Z3 DestructSolver(true); }
/// <summary> /// This method searches for a corresponding methods in the dynamically loaded assemblies and calls it if found. It prefers due to performance reasons the Microsoft Solver Foundation implementation. /// </summary> /// <param name="config">The (partial) configuration which needs to be expaned to be valid.</param> /// <param name="vm">Variability model containing all options and their constraints.</param> /// <param name="minimize">If true, we search for the smallest (in terms of selected options) valid configuration. If false, we search for the largest one.</param> /// <param name="unWantedOptions">Binary options that we do not want to become part of the configuration. Might be part if there is no other valid configuration without them.</param> /// <returns>The valid configuration (or null if there is none) that satisfies the VM and the goal.</returns> public List <BinaryOption> MinimizeConfig(List <BinaryOption> config, VariabilityModel vm, bool minimize, List <BinaryOption> unWantedOptions) { List <BoolExpr> variables; Dictionary <BoolExpr, BinaryOption> termToOption; Dictionary <BinaryOption, BoolExpr> optionToTerm; Tuple <Context, BoolExpr> z3Tuple = Z3Solver.GetInitializedBooleanSolverSystem(out variables, out optionToTerm, out termToOption, vm, this.henard); Context z3Context = z3Tuple.Item1; BoolExpr z3Constraints = z3Tuple.Item2; List <BoolExpr> constraints = new List <BoolExpr>(); constraints.Add(z3Constraints); //Feature Selection foreach (BinaryOption binOpt in config) { BoolExpr term = optionToTerm[binOpt]; constraints.Add(term); } Model model = null; if (minimize == true) { //Defining Goals ArithExpr[] optimizationGoals = new ArithExpr[variables.Count]; for (int r = 0; r < variables.Count; r++) { BinaryOption currOption = termToOption[variables[r]]; ArithExpr numericVariable = z3Context.MkIntConst(currOption.Name); int weight = 1; if (unWantedOptions != null && (unWantedOptions.Contains(termToOption[variables[r]]) && !config.Contains(termToOption[variables[r]]))) { weight = 1000; } constraints.Add(z3Context.MkEq(numericVariable, z3Context.MkITE(variables[r], z3Context.MkInt(weight), z3Context.MkInt(0)))); optimizationGoals[r] = numericVariable; } // For minimization, we need the class 'Optimize' Optimize optimizer = z3Context.MkOptimize(); optimizer.Assert(constraints.ToArray()); optimizer.MkMinimize(z3Context.MkAdd(optimizationGoals)); if (optimizer.Check() != Status.SATISFIABLE) { return(new List <BinaryOption>()); } else { model = optimizer.Model; } } else { // Return the first configuration returned by the solver Microsoft.Z3.Solver solver = z3Context.MkSolver(); // TODO: The following line works for z3Solver version >= 4.6.0 //solver.Set (RANDOM_SEED, z3RandomSeed); Params solverParameter = z3Context.MkParams(); solverParameter.Add(RANDOM_SEED, z3RandomSeed); solver.Parameters = solverParameter; solver.Assert(constraints.ToArray()); if (solver.Check() != Status.SATISFIABLE) { return(new List <BinaryOption>()); } else { model = solver.Model; } } List <BinaryOption> result = RetrieveConfiguration(variables, model, termToOption); return(result); }
/// <summary> /// Based on a given (partial) configuration and a variability, we aim at finding all optimally maximal or minimal (in terms of selected binary options) configurations. /// </summary> /// <param name="config">The (partial) configuration which needs to be expaned to be valid.</param> /// <param name="vm">Variability model containing all options and their constraints.</param> /// <param name="minimize">If true, we search for the smallest (in terms of selected options) valid configuration. If false, we search for the largest one.</param> /// <param name="unwantedOptions">Binary options that we do not want to become part of the configuration. Might be part if there is no other valid configuration without them</param> /// <returns>A list of configurations that satisfies the VM and the goal (or null if there is none).</returns> public List <List <BinaryOption> > MaximizeConfig(List <BinaryOption> config, VariabilityModel vm, bool minimize, List <BinaryOption> unwantedOptions) { List <List <BinaryOption> > optimalConfigurations = new List <List <BinaryOption> >(); List <BoolExpr> variables; Dictionary <BoolExpr, BinaryOption> termToOption; Dictionary <BinaryOption, BoolExpr> optionToTerm; Tuple <Context, BoolExpr> z3Tuple = Z3Solver.GetInitializedBooleanSolverSystem(out variables, out optionToTerm, out termToOption, vm, this.henard); Context z3Context = z3Tuple.Item1; BoolExpr z3Constraints = z3Tuple.Item2; List <BoolExpr> constraints = new List <BoolExpr>(); constraints.Add(z3Constraints); List <BoolExpr> requireConfigs = new List <BoolExpr>(); if (config != null) { foreach (BinaryOption option in config) { requireConfigs.Add(optionToTerm[option]); } constraints.Add(z3Context.MkAnd(requireConfigs.ToArray())); } ArithExpr[] optimizationGoals = new ArithExpr[variables.Count]; for (int r = 0; r < variables.Count; r++) { BinaryOption currOption = termToOption[variables[r]]; ArithExpr numericVariable = z3Context.MkIntConst(currOption.Name); int weight; if (minimize) { weight = 1; } else { weight = -1; } if (unwantedOptions != null && (unwantedOptions.Contains(termToOption[variables[r]]) && !config.Contains(termToOption[variables[r]]))) { weight = 10000; } constraints.Add(z3Context.MkEq(numericVariable, z3Context.MkITE(variables[r], z3Context.MkInt(weight), z3Context.MkInt(0)))); optimizationGoals[r] = numericVariable; } Optimize optimizer = z3Context.MkOptimize(); optimizer.Assert(constraints.ToArray()); optimizer.MkMinimize(z3Context.MkAdd(optimizationGoals)); int bestSize = 0; int currentSize = 0; while (optimizer.Check() == Status.SATISFIABLE && currentSize >= bestSize) { Model model = optimizer.Model; List <BinaryOption> solution = RetrieveConfiguration(variables, model, termToOption); currentSize = solution.Count; if (currentSize >= bestSize) { optimalConfigurations.Add(solution); } if (bestSize == 0) { bestSize = solution.Count; } currentSize = solution.Count; optimizer.Assert(z3Context.MkNot(Z3Solver.ConvertConfiguration(z3Context, solution, optionToTerm, vm))); } return(optimalConfigurations); }