/// <summary>
        /// Parses a z3 solution into a configuration.
        /// This method also supports numeric variables.
        /// </summary>
        /// <param name="variables">List of all variables in the z3 context.</param>
        /// <param name="model">Solution of the context.</param>
        /// <param name="termToOption">Map from variables to binary options.</param>
        /// <param name="optionsToConsider">The options that are considered for the solution.</param>
        /// <returns>Configuration parsed from the solution.</returns>
        public static Tuple <List <BinaryOption>, Dictionary <NumericOption, double> > RetrieveConfiguration(
            List <Expr> variables, Model model, Dictionary <Expr, ConfigurationOption> termToOption,
            List <ConfigurationOption> optionsToConsider = null)
        {
            List <BinaryOption> binOpts = new List <BinaryOption>();
            Dictionary <NumericOption, double> config = new Dictionary <NumericOption, double>();

            foreach (Expr variable in variables)
            {
                if (optionsToConsider != null && !optionsToConsider.Contains(termToOption[variable]))
                {
                    continue;
                }

                Expr allocation = model.Eval(variable, completion: true);
                if (allocation.GetType() == typeof(BoolExpr))
                {
                    BoolExpr boolExpr = (BoolExpr)allocation;
                    if (boolExpr.IsTrue)
                    {
                        binOpts.Add((BinaryOption)termToOption[variable]);
                    }
                }
                else
                {
                    // In this case, we have a numeric variable
                    FPNum fpNum = (FPNum)allocation;

                    config.Add((NumericOption)termToOption[variable], Z3Solver.lookUpNumericValue(fpNum.ToString()));
                }
            }
            return(new Tuple <List <BinaryOption>, Dictionary <NumericOption, double> >(binOpts, config));
        }
        /// <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);
            }
        }
Exemple #3
0
        /// <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>
        /// 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);
        }
Exemple #5
0
        private void InitializeZ3Cache(VariabilityModel vm, int numberSelectedFeatures)
        {
            Dictionary <BoolExpr, BinaryOption> termToOption;

            Microsoft.Z3.Solver                 solver;
            Tuple <Context, BoolExpr>           z3Tuple;
            List <BoolExpr>                     variables;
            Dictionary <BinaryOption, BoolExpr> optionToTerm;
            Context z3Context;

            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);

            // 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);
        }
        public bool checkConfigurationSAT(List <BinaryOption> config, VariabilityModel vm, bool partialConfiguration)
        {
            List <BoolExpr> variables;
            Dictionary <BoolExpr, BinaryOption> termToOption;
            Dictionary <BinaryOption, BoolExpr> optionToTerm;
            Tuple <Context, BoolExpr>           z3Tuple = Z3Solver.GetInitializedBooleanSolverSystem(out variables, out optionToTerm, out termToOption, vm, false);
            Context  z3Context     = z3Tuple.Item1;
            BoolExpr z3Constraints = z3Tuple.Item2;

            List <BoolExpr> constraints = new List <BoolExpr>();

            Microsoft.Z3.Solver solver = z3Context.MkSolver();
            solver.Assert(z3Constraints);
            solver.Assert(Z3Solver.ConvertConfiguration(z3Context, config, optionToTerm, vm, partialConfiguration));

            if (solver.Check() == Status.SATISFIABLE)
            {
                return(true);
            }
            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 <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>
        /// 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);
        }
        public bool checkConfigurationSAT(Configuration c, VariabilityModel vm, bool partialConfiguration = false)
        {
            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;

            List <Expr> constraints = new List <Expr>();

            Microsoft.Z3.Solver solver = z3Context.MkSolver();
            solver.Assert(z3Constraints);

            solver.Assert(Z3Solver.ConvertConfiguration(z3Context, c.getBinaryOptions(BinaryOption.BinaryValue.Selected), optionToTerm, vm, partialConfiguration, c.NumericOptions));

            if (solver.Check() == Status.SATISFIABLE)
            {
                return(true);
            }
            return(false);
        }
Exemple #10
0
        /// <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>
        /// 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);
        }
        /// <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 &lt; 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);
        }