/// <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 <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; 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(z3Constraints); while (solver.Check() == Status.SATISFIABLE) { Model model = solver.Model; List <BinaryOption> binOpts = RetrieveConfiguration(variables, model, termToOption, optionsToConsider); Configuration c = new Configuration(binOpts); // Check if the non-boolean constraints are satisfied if (vm.configurationIsValid(c) && !VariantGenerator.IsInConfigurationFile(c, allConfigurations) && VariantGenerator.FulfillsMixedConstraints(c, vm)) { allConfigurations.Add(c); } solver.Push(); solver.Assert(Z3Solver.NegateExpr(z3Context, Z3Solver.ConvertConfiguration(z3Context, binOpts, optionToTerm, vm))); } solver.Push(); solver.Pop(Convert.ToUInt32(allConfigurations.Count() + 1)); return(allConfigurations); }
/// <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> /// Adds the given configuration as a constraint to the solver. /// </summary> /// <param name="vm">The variability model</param> /// <param name="configToExclude">The configuration to exclude</param> /// <param name="numberSelectedFeatures">The number of features to be selected</param> public void AddConstraint(VariabilityModel vm, Configuration configToExclude, int numberSelectedFeatures) { if (!constraintsCache.ContainsKey(numberSelectedFeatures)) { constraintsCache[numberSelectedFeatures] = new HashSet <Configuration>(); } if (constraintsCache[numberSelectedFeatures].Contains(configToExclude)) { return; } constraintsCache[numberSelectedFeatures].Add(configToExclude); if (_z3Cache == null) { _z3Cache = new Dictionary <int, Z3Cache>(); } Dictionary <BinaryOption, BoolExpr> optionToTerm; Context z3Context; Microsoft.Z3.Solver solver; if (!this._z3Cache.Keys.Contains(numberSelectedFeatures)) { InitializeZ3Cache(vm, numberSelectedFeatures); } Z3Cache cache = this._z3Cache[numberSelectedFeatures]; z3Context = cache.GetContext(); solver = cache.GetSolver(); optionToTerm = cache.GetOptionToTermMapping(); // Add the previous configurations as constraints solver.Assert(Z3Solver.NegateExpr(z3Context, Z3Solver.ConvertConfiguration(z3Context, configToExclude.getBinaryOptions(BinaryOption.BinaryValue.Selected), optionToTerm, vm))); // Create a new backtracking point for the next run solver.Push(); }
public List <BinaryOption> GenerateConfigurationFromBucket(VariabilityModel vm, int numberSelectedFeatures, Dictionary <List <BinaryOption>, int> featureWeight, Configuration lastSampledConfiguration) { if (_z3Cache == null) { _z3Cache = new Dictionary <int, Z3Cache>(); } List <KeyValuePair <List <BinaryOption>, int> > featureRanking; if (featureWeight != null) { featureRanking = featureWeight.ToList(); featureRanking.Sort((first, second) => first.Value.CompareTo(second.Value)); } else { featureRanking = new List <KeyValuePair <List <BinaryOption>, int> >(); } List <BoolExpr> variables = null; Dictionary <BoolExpr, BinaryOption> termToOption = null; Dictionary <BinaryOption, BoolExpr> optionToTerm = null; Tuple <Context, BoolExpr> z3Tuple; Context z3Context; Microsoft.Z3.Solver solver; // Reuse the solver if it is already in the cache if (this._z3Cache.Keys.Contains(numberSelectedFeatures)) { Z3Cache cache = this._z3Cache[numberSelectedFeatures]; z3Context = cache.GetContext(); solver = cache.GetSolver(); variables = cache.GetVariables(); termToOption = cache.GetTermToOptionMapping(); optionToTerm = cache.GetOptionToTermMapping(); if (lastSampledConfiguration != null) { // Add the previous configurations as constraints solver.Assert(Z3Solver.NegateExpr(z3Context, Z3Solver.ConvertConfiguration(z3Context, lastSampledConfiguration.getBinaryOptions(BinaryOption.BinaryValue.Selected), optionToTerm, vm))); // Create a new backtracking point for the next run solver.Push(); } } else { z3Tuple = Z3Solver.GetInitializedBooleanSolverSystem(out variables, out optionToTerm, out termToOption, vm, this.henard); z3Context = z3Tuple.Item1; BoolExpr z3Constraints = z3Tuple.Item2; 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(z3Constraints); if (lastSampledConfiguration != null) { // Add the previous configurations as constraints solver.Assert(Z3Solver.NegateExpr(z3Context, Z3Solver.ConvertConfiguration(z3Context, lastSampledConfiguration.getBinaryOptions(BinaryOption.BinaryValue.Selected), optionToTerm, vm))); } // The goal of this method is, to have an exact number of features selected // Therefore, initialize an integer array with the value '1' for the pseudo-boolean equal function int[] neutralWeights = new int[variables.Count]; for (int i = 0; i < variables.Count; i++) { neutralWeights[i] = 1; } solver.Assert(z3Context.MkPBEq(neutralWeights, variables.ToArray(), numberSelectedFeatures)); // Create a backtracking point before adding the optimization goal solver.Push(); this._z3Cache[numberSelectedFeatures] = new Z3Cache(z3Context, solver, variables, optionToTerm, termToOption); } // Check if there is still a solution available by finding the first satisfiable configuration if (solver.Check() == Status.SATISFIABLE) { Model model = solver.Model; List <BinaryOption> possibleSolution = RetrieveConfiguration(variables, model, termToOption); // Disable finding a configuration where the least frequent feature/feature combinations are selected // if no featureWeight is given. List <BinaryOption> approximateOptimal = null; if (featureRanking.Count != 0) { approximateOptimal = WeightMinimizer .getSmallWeightConfig(featureRanking, this._z3Cache[numberSelectedFeatures], vm); } if (approximateOptimal == null) { return(possibleSolution); } else { return(approximateOptimal); } } else { return(null); } }
/// <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(); // TODO: The following line works for z3Solver version >= 4.6.0 //solver.Set (RANDOM_SEED, z3RandomSeed); Params solverParameter = z3Context.MkParams(); if (henard) { solverParameter.Add(RANDOM_SEED, NextUInt(random)); } else { solverParameter.Add(RANDOM_SEED, z3RandomSeed); } s.Parameters = solverParameter; 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)); solverParameter = z3Context.MkParams(); solverParameter.Add(RANDOM_SEED, NextUInt(random)); s.Parameters = solverParameter; 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); }