/// <summary> /// Generates all valid combinations of all configuration options in the given model. /// </summary> /// <param name="vm">the variability model containing the binary options and their constraints</param> /// <param name="optionsToConsider">the options that should be considered. All other options are ignored</param> /// <returns>Returns a list of <see cref="Configuration"/></returns> public List <Configuration> GenerateAllVariants(VariabilityModel vm, List <ConfigurationOption> optionsToConsider) { List <Configuration> allConfigurations = new List <Configuration>(); List <Expr> variables; Dictionary <Expr, ConfigurationOption> termToOption; Dictionary <ConfigurationOption, Expr> optionToTerm; Tuple <Context, BoolExpr> z3Tuple = Z3Solver.GetInitializedSolverSystem(out variables, out optionToTerm, out termToOption, vm); Context z3Context = z3Tuple.Item1; BoolExpr z3Constraints = z3Tuple.Item2; Microsoft.Z3.Solver solver = z3Context.MkSolver(); solver.Set(RANDOM_SEED, z3RandomSeed); solver.Assert(z3Constraints); while (solver.Check() == Status.SATISFIABLE) { Model model = solver.Model; Tuple <List <BinaryOption>, Dictionary <NumericOption, double> > confOpts = RetrieveConfiguration(variables, model, termToOption, optionsToConsider); Configuration c = new Configuration(confOpts.Item1, confOpts.Item2); // Check if the non-boolean constraints are satisfied bool configIsValid = vm.configurationIsValid(c); bool isInConfigurationFile = !VariantGenerator.IsInConfigurationFile(c, allConfigurations); bool fulfillsMixedConstraintrs = VariantGenerator.FulfillsMixedConstraints(c, vm); if (configIsValid && isInConfigurationFile && fulfillsMixedConstraintrs) { allConfigurations.Add(c); } solver.Push(); solver.Assert(Z3Solver.NegateExpr(z3Context, Z3Solver.ConvertConfiguration(z3Context, confOpts.Item1, optionToTerm, vm, numericValues: confOpts.Item2))); } solver.Push(); solver.Pop(Convert.ToUInt32(allConfigurations.Count() + 1)); return(allConfigurations); }
/// <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(); solver.Set(RANDOM_SEED, z3RandomSeed); 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> /// Generates up to n solutions of the given variability model. /// Note that this method could also generate less than n solutions if the variability model does not contain sufficient solutions. /// Moreover, in the case that <code>n < 0</code>, all solutions are generated. /// </summary> /// <param name="vm">The <see cref="VariabilityModel"/> to obtain solutions for.</param> /// <param name="n">The number of solutions to obtain.</param> /// <returns>A list of configurations, in which a configuration is a list of SELECTED binary options.</returns> public List <List <BinaryOption> > GenerateUpToNFast(VariabilityModel vm, int n) { // Use the random seed to produce new random seeds Random random = new Random(Convert.ToInt32(z3RandomSeed)); 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, random.Next()); Context z3Context = z3Tuple.Item1; BoolExpr z3Constraints = z3Tuple.Item2; List <BoolExpr> excludedConfigurations = new List <BoolExpr>(); List <BoolExpr> constraints = Z3Solver.lastConstraints; List <List <BinaryOption> > configurations = new List <List <BinaryOption> >(); Microsoft.Z3.Solver s = z3Context.MkSolver(); if (henard) { s.Set(RANDOM_SEED, NextUInt(random)); } else { s.Set(RANDOM_SEED, z3RandomSeed); } s.Assert(z3Constraints); s.Push(); Model model = null; while (s.Check() == Status.SATISFIABLE && (configurations.Count < n || n < 0)) { model = s.Model; List <BinaryOption> config = RetrieveConfiguration(variables, model, termToOption); configurations.Add(config); if (henard) { BoolExpr newConstraint = Z3Solver.NegateExpr(z3Context, Z3Solver.ConvertConfiguration(z3Context, config, optionToTerm, vm)); excludedConfigurations.Add(newConstraint); Dictionary <BoolExpr, BinaryOption> oldTermToOption = termToOption; // Now, initialize a new one for the next configuration z3Tuple = Z3Solver.GetInitializedBooleanSolverSystem(out variables, out optionToTerm, out termToOption, vm, this.henard, random.Next()); z3Context = z3Tuple.Item1; z3Constraints = z3Tuple.Item2; s = z3Context.MkSolver(); s.Set(RANDOM_SEED, NextUInt(random)); constraints = Z3Solver.lastConstraints; excludedConfigurations = Z3Solver.ConvertConstraintsToNewContext(oldTermToOption, optionToTerm, excludedConfigurations, z3Context); constraints.AddRange(excludedConfigurations); s.Assert(z3Context.MkAnd(Z3Solver.Shuffle(constraints, new Random(random.Next())))); s.Push(); } else { s.Add(Z3Solver.NegateExpr(z3Context, Z3Solver.ConvertConfiguration(z3Context, config, optionToTerm, vm))); } } return(configurations); }