OR-Tools  9.6
optimization.h
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 #ifndef OR_TOOLS_SAT_OPTIMIZATION_H_
15 #define OR_TOOLS_SAT_OPTIMIZATION_H_
16 
17 #include <functional>
18 #include <vector>
19 
20 #include "absl/random/bit_gen_ref.h"
21 #include "ortools/sat/boolean_problem.pb.h"
23 #include "ortools/sat/integer.h"
25 #include "ortools/sat/model.h"
27 #include "ortools/sat/sat_base.h"
28 #include "ortools/sat/sat_parameters.pb.h"
29 #include "ortools/sat/sat_solver.h"
31 
32 namespace operations_research {
33 namespace sat {
34 
35 // Like MinimizeCore() with a slower but strictly better heuristic. This
36 // algorithm should produce a minimal core with respect to propagation. We put
37 // each literal of the initial core "last" at least once, so if such literal can
38 // be inferred by propagation by any subset of the other literal, it will be
39 // removed.
40 //
41 // Note that the literal of the minimized core will stay in the same order.
42 //
43 // TODO(user): Avoid spending too much time trying to minimize a core.
44 void MinimizeCoreWithPropagation(TimeLimit* limit, SatSolver* solver,
45  std::vector<Literal>* core);
46 
47 // Because the Solve*() functions below are also used in scripts that requires a
48 // special output format, we use this to tell them whether or not to use the
49 // default logging framework or simply stdout. Most users should just use
50 // DEFAULT_LOG.
52 
53 // All the Solve*() functions below reuse the SatSolver::Status with a slightly
54 // different meaning:
55 // - FEASIBLE: The problem has been solved to optimality.
56 // - INFEASIBLE: Same meaning, the decision version is already unsat.
57 // - LIMIT_REACHED: we may have some feasible solution (if solution is
58 // non-empty), but the optimality is not proven.
59 
60 // Implements the "Fu & Malik" algorithm described in:
61 // Zhaohui Fu, Sharad Malik, "On solving the Partial MAX-SAT problem", 2006,
62 // International Conference on Theory and Applications of Satisfiability
63 // Testing. (SAT’06), LNCS 4121.
64 //
65 // This algorithm requires all the objective weights to be the same (CHECKed)
66 // and currently only works on minimization problems. The problem is assumed to
67 // be already loaded into the given solver.
68 //
69 // TODO(user): double-check the correctness if the objective coefficients are
70 // negative.
72  const LinearBooleanProblem& problem,
73  SatSolver* solver,
74  std::vector<bool>* solution);
75 
76 // The WPM1 algorithm is a generalization of the Fu & Malik algorithm to
77 // weighted problems. Note that if all objective weights are the same, this is
78 // almost the same as SolveWithFuMalik() but the encoding of the constraints is
79 // slightly different.
80 //
81 // Ansotegui, C., Bonet, M.L., Levy, J.: Solving (weighted) partial MaxSAT
82 // through satisfiability testing. In: Proc. of the 12th Int. Conf. on Theory and
83 // Applications of Satisfiability Testing (SAT’09). pp. 427-440 (2009)
85  const LinearBooleanProblem& problem,
86  SatSolver* solver, std::vector<bool>* solution);
87 
88 // Solves num_times the decision version of the given problem with different
89 // random parameters. Keep the best solution (regarding the objective) and
90 // returns it in solution. The problem is assumed to be already loaded into the
91 // given solver.
93  LogBehavior log, const LinearBooleanProblem& problem, int num_times,
94  absl::BitGenRef random, SatSolver* solver, std::vector<bool>* solution);
95 
96 // Starts by solving the decision version of the given LinearBooleanProblem and
97 // then simply add a constraint to find a lower objective that the current best
98 // solution and repeat until the problem becomes unsat.
99 //
100 // The problem is assumed to be already loaded into the given solver. If
101 // solution is initially a feasible solution, the search will starts from there.
102 // solution will be updated with the best solution found so far.
104  const LinearBooleanProblem& problem,
105  SatSolver* solver,
106  std::vector<bool>* solution);
107 
108 // Similar algorithm as the one used by qmaxsat, this is a linear scan with the
109 // at-most k constraint encoded in SAT. This only works on problems with
110 // constant weights.
112  LogBehavior log, const LinearBooleanProblem& problem, SatSolver* solver,
113  std::vector<bool>* solution);
114 
115 // This is an original algorithm. It is a mix between the cardinality encoding
116 // and the Fu & Malik algorithm. It also works on general weighted instances.
118  LogBehavior log, const LinearBooleanProblem& problem, SatSolver* solver,
119  std::vector<bool>* solution);
120 
121 // Model-based API to minimize a given IntegerVariable by solving a sequence of
122 // decision problem. Each problem is solved using SolveIntegerProblem(). Returns
123 // the status of the last solved decision problem.
124 //
125 // The feasible_solution_observer function will be called each time a new
126 // feasible solution is found.
127 //
128 // Note that this function will resume the search from the current state of the
129 // solver, and it is up to the client to backtrack to the root node if needed.
131  IntegerVariable objective_var,
132  const std::function<void()>& feasible_solution_observer, Model* model);
133 
134 // Use a low conflict limit and performs a binary search to try to restrict the
135 // domain of objective_var.
137  IntegerVariable objective_var,
138  const std::function<void()>& feasible_solution_observer, Model* model);
139 
140 // Transforms the given linear expression so that:
141 // - duplicate terms are merged.
142 // - terms with a literal and its negation are merged.
143 // - all weight are positive.
144 //
145 // TODO(user): Merge this with similar code like
146 // ComputeBooleanLinearExpressionCanonicalForm().
147 void PresolveBooleanLinearExpression(std::vector<Literal>* literals,
148  std::vector<Coefficient>* coefficients,
149  Coefficient* offset);
150 
151 // Same as MinimizeIntegerVariableWithLinearScanAndLazyEncoding() but use
152 // a core-based approach instead. Note that the given objective_var is just used
153 // for reporting the lower-bound/upper-bound and do not need to be linked with
154 // its linear representation.
155 //
156 // Unlike MinimizeIntegerVariableWithLinearScanAndLazyEncoding() this function
157 // just return the last solver status. In particular if it is INFEASIBLE but
158 // feasible_solution_observer() was called, it means we are at OPTIMAL.
160  public:
161  CoreBasedOptimizer(IntegerVariable objective_var,
162  const std::vector<IntegerVariable>& variables,
163  const std::vector<IntegerValue>& coefficients,
164  std::function<void()> feasible_solution_observer,
165  Model* model);
166 
167  // TODO(user): Change the algo slighlty to allow resuming from the last
168  // aborted position. Currently, the search is "resumable", but it will restart
169  // some of the work already done, so it might just never find anything.
171 
172  // A different way to encode the objective as core are found.
173  //
174  // If the vector if literals is passed it will use that, otherwise it will
175  // encode the passed integer variables. In both cases, the vector used should
176  // be of the same size as the coefficients vector.
177  //
178  // It seems to be more powerful, but it isn't completely implemented yet.
179  // TODO(user):
180  // - Support resuming for interleaved search.
181  // - Implement all core heurisitics.
183  const std::vector<Literal>& literals,
184  const std::vector<IntegerVariable>& vars,
185  const std::vector<Coefficient>& coefficients, Coefficient offset);
186 
187  private:
188  CoreBasedOptimizer(const CoreBasedOptimizer&) = delete;
189  CoreBasedOptimizer& operator=(const CoreBasedOptimizer&) = delete;
190 
191  struct ObjectiveTerm {
192  IntegerVariable var;
193  IntegerValue weight;
194  int depth; // Only for logging/debugging.
195  IntegerValue old_var_lb;
196 
197  // An upper bound on the optimal solution if we were to optimize only this
198  // term. This is used by the cover optimization code.
199  IntegerValue cover_ub;
200  };
201 
202  // This will be called each time a feasible solution is found. Returns false
203  // if a conflict was detected while trying to constrain the objective to a
204  // smaller value.
205  bool ProcessSolution();
206 
207  // Use the gap an implied bounds to propagated the bounds of the objective
208  // variables and of its terms.
209  bool PropagateObjectiveBounds();
210 
211  // Heuristic that aim to find the "real" lower bound of the objective on each
212  // core by using a linear scan optimization approach.
213  bool CoverOptimization();
214 
215  // Computes the next stratification threshold.
216  // Sets it to zero if all the assumptions where already considered.
217  void ComputeNextStratificationThreshold();
218 
219  // If we have an "at most one can be false" between literals with a positive
220  // cost, you then know that at least n - 1 will contribute to the cost, and
221  // you can increase the objective lower bound. This is the same as having
222  // a real "at most one" constraint on the negation of such literals.
223  //
224  // This detects such "at most ones" and rewrite the objective accordingly.
225  // For each at most one, the rewrite create a new Boolean variable and update
226  // the cost so that the trivial objective lower bound reflect the increase.
227  //
228  // TODO(user) : Code that as a general presolve rule? I am not sure adding
229  // the extra Booleans is always a good idea though. Especially since the LP
230  // will see the same lower bound that what is computed by this.
231  void PresolveObjectiveWithAtMostOne(std::vector<Literal>* literals,
232  std::vector<Coefficient>* coefficients,
233  Coefficient* offset);
234 
235  SatParameters* parameters_;
236  SatSolver* sat_solver_;
237  TimeLimit* time_limit_;
238  BinaryImplicationGraph* implications_;
239  IntegerTrail* integer_trail_;
240  IntegerEncoder* integer_encoder_;
241  Model* model_;
242 
243  IntegerVariable objective_var_;
244  std::vector<ObjectiveTerm> terms_;
245  IntegerValue stratification_threshold_;
246  std::function<void()> feasible_solution_observer_;
247 
248  // This is used to not add the objective equation more than once if we
249  // solve in "chunk".
250  bool already_switched_to_linear_scan_ = false;
251 
252  // Set to true when we need to abort early.
253  //
254  // TODO(user): This is only used for the stop after first solution parameter
255  // which should likely be handled differently by simply using the normal way
256  // to stop a solver from the feasible solution callback.
257  bool stop_ = false;
258 };
259 
260 } // namespace sat
261 } // namespace operations_research
262 
263 #endif // OR_TOOLS_SAT_OPTIMIZATION_H_
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
CoreBasedOptimizer(IntegerVariable objective_var, const std::vector< IntegerVariable > &variables, const std::vector< IntegerValue > &coefficients, std::function< void()> feasible_solution_observer, Model *model)
SatSolver::Status OptimizeWithSatEncoding(const std::vector< Literal > &literals, const std::vector< IntegerVariable > &vars, const std::vector< Coefficient > &coefficients, Coefficient offset)
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
IntVar * var
Definition: expr_array.cc:1874
absl::Span< const double > coefficients
GRBmodel * model
std::tuple< int64_t, int64_t, const double > Coefficient
void RestrictObjectiveDomainWithBinarySearch(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
SatSolver::Status SolveWithCardinalityEncodingAndCore(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
void PresolveBooleanLinearExpression(std::vector< Literal > *literals, std::vector< Coefficient > *coefficients, Coefficient *offset)
SatSolver::Status SolveWithLinearScan(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
SatSolver::Status SolveWithRandomParameters(LogBehavior log, const LinearBooleanProblem &problem, int num_times, absl::BitGenRef random, SatSolver *solver, std::vector< bool > *solution)
SatSolver::Status SolveWithWPM1(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
void MinimizeCoreWithPropagation(TimeLimit *limit, SatSolver *solver, std::vector< Literal > *core)
SatSolver::Status SolveWithFuMalik(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
SatSolver::Status SolveWithCardinalityEncoding(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
SatSolver::Status MinimizeIntegerVariableWithLinearScanAndLazyEncoding(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
Collection of objects used to extend the Constraint Solver library.
int64_t weight
Definition: pack.cc:510