/// <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 #2
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>
        /// 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);
        }
Exemple #4
0
        /// <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);
        }