/// <summary> /// Generates up to n valid binary combinations of all binary configuration options in the given model. /// In case n < 0 all valid binary combinations will be generated. /// </summary> /// <param name="m">The variability model containing the binary options and their constraints.</param> /// <param name="n">The maximum number of samples that will be generated.</param> /// <returns>Returns a list of configurations, in which a configuration is a list of SELECTED binary options (deselected options are not present)</returns> public List <List <BinaryOption> > GenerateUpToNFast(VariabilityModel m, int n) { List <List <BinaryOption> > configurations = new List <List <BinaryOption> >(); List <CspTerm> variables = new List <CspTerm>(); Dictionary <BinaryOption, CspTerm> elemToTerm = new Dictionary <BinaryOption, CspTerm>(); Dictionary <CspTerm, BinaryOption> termToElem = new Dictionary <CspTerm, BinaryOption>(); ConstraintSystem S = CSPsolver.getConstraintSystem(out variables, out elemToTerm, out termToElem, m); ConstraintSolverSolution soln = S.Solve(); // TODO: Better solution than magic number? while (soln.HasFoundSolution && (configurations.Count < n || n < 0)) { List <BinaryOption> config = new List <BinaryOption>(); foreach (CspTerm cT in variables) { if (soln.GetIntegerValue(cT) == 1) { config.Add(termToElem[cT]); } } //THese should always be new configurations // if(!Configuration.containsBinaryConfiguration(configurations, config)) configurations.Add(config); soln.GetNext(); } return(configurations); }
/// <summary> /// Generates all valid binary combinations of all binary configurations options in the given model /// </summary> /// <param name="vm">The variability model containing the binary options and their constraints.</param> /// <returns>Returns a list of configurations, in which a configuration is a list of SELECTED binary options (deselected options are not present)</returns> public List <List <BinaryOption> > generateAllVariantsFast(VariabilityModel vm) { List <List <BinaryOption> > configurations = new List <List <BinaryOption> >(); List <CspTerm> variables = new List <CspTerm>(); Dictionary <BinaryOption, CspTerm> elemToTerm = new Dictionary <BinaryOption, CspTerm>(); Dictionary <CspTerm, BinaryOption> termToElem = new Dictionary <CspTerm, BinaryOption>(); ConstraintSystem S = CSPsolver.getConstraintSystem(out variables, out elemToTerm, out termToElem, vm); ConstraintSolverSolution soln = S.Solve(); while (soln.HasFoundSolution) { List <BinaryOption> config = new List <BinaryOption>(); foreach (CspTerm cT in variables) { if (soln.GetIntegerValue(cT) == 1) { config.Add(termToElem[cT]); } } //THese should always be new configurations // if(!Configuration.containsBinaryConfiguration(configurations, config)) configurations.Add(config); soln.GetNext(); } return(configurations); }
/// <summary> /// Checks whether the boolean selection is valid w.r.t. the variability model. Does not check for numeric options' correctness. /// </summary> /// <param name="config">The list of binary options that are SELECTED (only selected options must occur in the list).</param> /// <param name="vm">The variability model that represents the context of the configuration.</param> /// <param name="partialConfiguration">Whether the given list of options represents only a partial configuration. This means that options not in config might be additionally select to obtain a valid configuration.</param> /// <returns>True if it is a valid selection w.r.t. the VM, false otherwise</returns> public bool checkConfigurationSAT(List <BinaryOption> config, VariabilityModel vm, bool partialConfiguration) { List <CspTerm> variables = new List <CspTerm>(); Dictionary <BinaryOption, CspTerm> elemToTerm = new Dictionary <BinaryOption, CspTerm>(); Dictionary <CspTerm, BinaryOption> termToElem = new Dictionary <CspTerm, BinaryOption>(); ConstraintSystem S = CSPsolver.getConstraintSystem(out variables, out elemToTerm, out termToElem, vm); //Feature Selection foreach (BinaryOption binayOpt in elemToTerm.Keys) { CspTerm term = elemToTerm[binayOpt]; if (config.Contains(binayOpt)) { S.AddConstraints(S.Implies(S.True, term)); } else if (!partialConfiguration) { S.AddConstraints(S.Implies(S.True, S.Not(term))); } } ConstraintSolverSolution sol = S.Solve(); if (sol.HasFoundSolution) { return(true); } else { return(false); } }
/// <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. /// Idea: Encode this as a CSP problem. We aim at finding a configuration that maximizes a goal. Each option of the given configuration gets a large value assigned. All other options of the variability model gets a negative value assigned. /// We will further create a boolean constraint that forbids selecting the optionToBeRemoved. Now, we find an optimal valid configuration. /// </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) { List <CspTerm> variables = new List <CspTerm>(); Dictionary <BinaryOption, CspTerm> elemToTerm = new Dictionary <BinaryOption, CspTerm>(); Dictionary <CspTerm, BinaryOption> termToElem = new Dictionary <CspTerm, BinaryOption>(); ConstraintSystem S = CSPsolver.getConstraintSystem(out variables, out elemToTerm, out termToElem, vm); removedElements = new List <BinaryOption>(); //Forbid the selection of this configuration option CspTerm optionToRemove = elemToTerm[optionToBeRemoved]; S.AddConstraints(S.Implies(S.True, S.Not(optionToRemove))); //Defining Goals CspTerm[] finalGoals = new CspTerm[variables.Count]; int r = 0; foreach (var term in variables) { if (originalConfig.Contains(termToElem[term])) { finalGoals[r] = term * -1000; //Since we minimize, we put a large negative value of an option that is within the original configuration to increase chances that the option gets selected again } else { finalGoals[r] = variables[r] * 10000;//Positive number will lead to a small chance that an option gets selected when it is not in the original configuration } r++; } S.TryAddMinimizationGoals(S.Sum(finalGoals)); ConstraintSolverSolution soln = S.Solve(); List <BinaryOption> tempConfig = new List <BinaryOption>(); if (soln.HasFoundSolution && soln.Quality == ConstraintSolverSolution.SolutionQuality.Optimal) { tempConfig.Clear(); foreach (CspTerm cT in variables) { if (soln.GetIntegerValue(cT) == 1) { tempConfig.Add(termToElem[cT]); } } //Adding the options that have been removed from the original configuration foreach (var opt in originalConfig) { if (!tempConfig.Contains(opt)) { removedElements.Add(opt); } } return(tempConfig); } return(null); }
private void InitializeCache(VariabilityModel vm, int numberSelectedFeatures) { List <CspTerm> variables; Dictionary <CspTerm, BinaryOption> termToElem; ConstraintSystem S; Dictionary <BinaryOption, CspTerm> elemToTerm; // Build the constraint system S = CSPsolver.getConstraintSystem(out variables, out elemToTerm, out termToElem, vm); // The first goal of this method is, to have an exact number of features selected S.AddConstraints(S.ExactlyMofN(numberSelectedFeatures, variables.ToArray())); this._constraintSystemCache.Add(numberSelectedFeatures, new ConstraintSystemCache(S, variables, elemToTerm, termToElem)); }
/// <summary> /// Checks whether the boolean selection of a configuration is valid w.r.t. the variability model. Does not check for numeric options' correctness. /// </summary> /// <param name="c">The configuration that needs to be checked.</param> /// <param name="vm">The variability model that represents the context of the configuration.</param> /// <returns>True if it is a valid selection w.r.t. the VM, false otherwise</returns> public bool checkConfigurationSAT(Configuration c, VariabilityModel vm) { List <CspTerm> variables = new List <CspTerm>(); Dictionary <BinaryOption, CspTerm> elemToTerm = new Dictionary <BinaryOption, CspTerm>(); Dictionary <CspTerm, BinaryOption> termToElem = new Dictionary <CspTerm, BinaryOption>(); ConstraintSystem S = CSPsolver.getConstraintSystem(out variables, out elemToTerm, out termToElem, vm); //Feature Selection foreach (BinaryOption binayOpt in elemToTerm.Keys) { CspTerm term = elemToTerm[binayOpt]; if (c.getBinaryOptions(BinaryOption.BinaryValue.Selected).Contains(binayOpt)) { S.AddConstraints(S.Implies(S.True, term)); } else { S.AddConstraints(S.Implies(S.True, S.Not(term))); } } ConstraintSolverSolution sol = S.Solve(); if (sol.HasFoundSolution) { int count = 0; foreach (CspTerm cT in variables) { if (sol.GetIntegerValue(cT) == 1) { count++; } } //-1??? Needs testing TODO if (count - 1 != c.getBinaryOptions(BinaryOption.BinaryValue.Selected).Count) { return(false); } return(true); } else { return(false); } }
/// <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 <Configuration> sample = new List <Configuration>(); List <List <BinaryOption> > convertedSample = new List <List <BinaryOption> >(); sample.Add(new Configuration(minimalConfiguration)); convertedSample.Add(minimalConfiguration); List <CspTerm> variables = new List <CspTerm>(); Dictionary <BinaryOption, CspTerm> elemToTerm = new Dictionary <BinaryOption, CspTerm>(); Dictionary <CspTerm, BinaryOption> termToElem = new Dictionary <CspTerm, BinaryOption>(); while (sample.Count < numberToSample) { ConstraintSystem S = CSPsolver.getConstraintSystem(out variables, out elemToTerm, out termToElem, vm); addDistanceMaximiationGoal(sample, vm, elemToTerm, S, optionWeight); ConstraintSolverSolution sol = S.Solve(); if (sol.HasFoundSolution) { List <BinaryOption> solution = new List <BinaryOption>(); foreach (CspTerm cT in variables) { if (sol.GetIntegerValue(cT) == 1) { solution.Add(termToElem[cT]); } } S.ResetSolver(); convertedSample.Add(solution); sample.Add(new Configuration(solution)); } else { GlobalState.logInfo.logLine("No more solutions available."); return(convertedSample); } } return(convertedSample); }
/// <summary> /// Simulates a simple method to get valid configurations of binary options of a variability model. The randomness is simulated by the modulu value. /// We take only the modulu'th configuration into the result set based on the CSP solvers output. If modulu is larger than the number of valid variants, the result set is empty. /// </summary> /// <param name="vm">The variability model containing the binary options and their constraints.</param> /// <param name="treshold">Maximum number of configurations</param> /// <param name="modulu">Each configuration that is % modulu == 0 is taken to the result set</param> /// <returns>Returns a list of configurations, in which a configuration is a list of SELECTED binary options (deselected options are not present</returns> public List <List <BinaryOption> > GenerateRandomVariants(VariabilityModel vm, int treshold, int modulu) { List <CspTerm> variables = new List <CspTerm>(); Dictionary <BinaryOption, CspTerm> elemToTerm = new Dictionary <BinaryOption, CspTerm>(); Dictionary <CspTerm, BinaryOption> termToElem = new Dictionary <CspTerm, BinaryOption>(); ConstraintSystem S = CSPsolver.getConstraintSystem(out variables, out elemToTerm, out termToElem, vm); List <List <BinaryOption> > erglist = new List <List <BinaryOption> >(); ConstraintSolverSolution soln = S.Solve(); List <List <BinaryOption> > allConfigs = new List <List <BinaryOption> >(); while (soln.HasFoundSolution) { soln.GetNext(); List <BinaryOption> tempConfig = new List <BinaryOption>(); foreach (CspTerm cT in variables) { if (soln.GetIntegerValue(cT) == 1) { tempConfig.Add(termToElem[cT]); } } if (tempConfig.Contains(null)) { tempConfig.Remove(null); } allConfigs.Add(tempConfig); } Random r = new Random(modulu); for (int i = 0; i < treshold; i++) { erglist.Add(allConfigs[r.Next(allConfigs.Count)]); } return(erglist); }
/// <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 <CspTerm> variables = new List <CspTerm>(); Dictionary <BinaryOption, CspTerm> elemToTerm = new Dictionary <BinaryOption, CspTerm>(); Dictionary <CspTerm, BinaryOption> termToElem = new Dictionary <CspTerm, BinaryOption>(); ConstraintSystem S = CSPsolver.getConstraintSystem(out variables, out elemToTerm, out termToElem, vm); //Feature Selection if (config != null) { foreach (BinaryOption binOpt in config) { CspTerm term = elemToTerm[binOpt]; S.AddConstraints(S.Implies(S.True, term)); } } //Defining Goals CspTerm[] finalGoals = new CspTerm[variables.Count]; for (int r = 0; r < variables.Count; r++) { if (minimize == true) { BinaryOption binOpt = termToElem[variables[r]]; if (unwantedOptions != null && (unwantedOptions.Contains(binOpt) && !config.Contains(binOpt))) { finalGoals[r] = variables[r] * 10000; } else { // Element is part of an altnerative Group ... we want to select always the same option of the group, so we give different weights to the member of the group //Functionality deactivated... todo needs further handling /*if (binOpt.getAlternatives().Count != 0) * { * finalGoals[r] = variables[r] * (binOpt.getID() * 10); * } * else * {*/ finalGoals[r] = variables[r] * 1; //} // wenn in einer alternative, dann bekommt es einen wert nach seiner reihenfolge // id mal 10 } } else { finalGoals[r] = variables[r] * -1; // dynamic cost map } } S.TryAddMinimizationGoals(S.Sum(finalGoals)); ConstraintSolverSolution soln = S.Solve(); List <string> erg2 = new List <string>(); List <BinaryOption> tempConfig = new List <BinaryOption>(); List <List <BinaryOption> > resultConfigs = new List <List <BinaryOption> >(); while (soln.HasFoundSolution && soln.Quality == ConstraintSolverSolution.SolutionQuality.Optimal) { tempConfig.Clear(); foreach (CspTerm cT in variables) { if (soln.GetIntegerValue(cT) == 1) { tempConfig.Add(termToElem[cT]); } } if (minimize && tempConfig != null) { resultConfigs.Add(tempConfig); break; } if (!Configuration.containsBinaryConfiguration(resultConfigs, tempConfig)) { resultConfigs.Add(tempConfig); } soln.GetNext(); } return(resultConfigs); }
/// <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 <CspTerm> variables = new List <CspTerm>(); Dictionary <BinaryOption, CspTerm> elemToTerm = new Dictionary <BinaryOption, CspTerm>(); Dictionary <CspTerm, BinaryOption> termToElem = new Dictionary <CspTerm, BinaryOption>(); ConstraintSystem S = CSPsolver.getConstraintSystem(out variables, out elemToTerm, out termToElem, vm); //Feature Selection foreach (BinaryOption binOpt in config) { CspTerm term = elemToTerm[binOpt]; S.AddConstraints(S.Implies(S.True, term)); } //Defining Goals CspTerm[] finalGoals = new CspTerm[variables.Count]; for (int r = 0; r < variables.Count; r++) { if (minimize == true) { if (unWantedOptions != null && (unWantedOptions.Contains(termToElem[variables[r]]) && !config.Contains(termToElem[variables[r]]))) { finalGoals[r] = variables[r] * 100; } else { finalGoals[r] = variables[r] * 1; } } else { finalGoals[r] = variables[r] * -1; // dynamic cost map } } S.TryAddMinimizationGoals(S.Sum(finalGoals)); ConstraintSolverSolution soln = S.Solve(); List <string> erg2 = new List <string>(); List <BinaryOption> tempConfig = new List <BinaryOption>(); while (soln.HasFoundSolution) { tempConfig.Clear(); foreach (CspTerm cT in variables) { if (soln.GetIntegerValue(cT) == 1) { tempConfig.Add(termToElem[cT]); } } if (minimize && tempConfig != null) { break; } soln.GetNext(); } return(tempConfig); }
/// <summary> /// This method has the objective to sample a configuration where n features are selected /// </summary> /// <returns>The first fitting configuration.</returns> /// <param name="vm">The variability model.</param> /// <param name="numberSelectedFeatures">The number of features that should be selected.</param> /// <param name="featureWeight">The weight of the features to minimize.</param> /// <param name="sampledConfigurations">The sampled configurations until now.</param> public List <BinaryOption> GenerateConfigurationFromBucket(VariabilityModel vm, int numberSelectedFeatures, Dictionary <List <BinaryOption>, int> featureWeight, Configuration lastSampledConfiguration) { if (this._constraintSystemCache == null) { this._constraintSystemCache = new Dictionary <int, ConstraintSystemCache>(); } List <CspTerm> variables; Dictionary <BinaryOption, CspTerm> elemToTerm; Dictionary <CspTerm, BinaryOption> termToElem; ConstraintSystem S; if (this._constraintSystemCache.Keys.Contains(numberSelectedFeatures)) { variables = _constraintSystemCache[numberSelectedFeatures].GetVariables(); elemToTerm = _constraintSystemCache[numberSelectedFeatures].GetElemToTermMapping(); termToElem = _constraintSystemCache[numberSelectedFeatures].GetTermToElemMapping(); S = _constraintSystemCache[numberSelectedFeatures].GetConstraintSystem(); S.ResetSolver(); S.RemoveAllMinimizationGoals(); // Add the missing configurations AddBinaryConfigurationsToConstraintSystem(vm, S, lastSampledConfiguration, elemToTerm); } else { variables = new List <CspTerm>(); elemToTerm = new Dictionary <BinaryOption, CspTerm>(); termToElem = new Dictionary <CspTerm, BinaryOption>(); // Build the constraint system S = CSPsolver.getConstraintSystem(out variables, out elemToTerm, out termToElem, vm); // The first goal of this method is, to have an exact number of features selected S.AddConstraints(S.ExactlyMofN(numberSelectedFeatures, variables.ToArray())); if (lastSampledConfiguration != null) { // Add the previous configurations as constraints AddBinaryConfigurationsToConstraintSystem(vm, S, lastSampledConfiguration, elemToTerm); } this._constraintSystemCache.Add(numberSelectedFeatures, new ConstraintSystemCache(S, variables, elemToTerm, termToElem)); } // Next, solve the constraint system ConstraintSolverSolution soln = S.Solve(); List <BinaryOption> tempConfig = new List <BinaryOption>(); if (soln.HasFoundSolution) { tempConfig.Clear(); foreach (CspTerm cT in variables) { if (soln.GetIntegerValue(cT) == 1) { tempConfig.Add(termToElem[cT]); } } } else { return(null); } return(tempConfig); }