OR-Tools  9.6
cp_model_presolve.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_CP_MODEL_PRESOLVE_H_
15 #define OR_TOOLS_SAT_CP_MODEL_PRESOLVE_H_
16 
17 #include <array>
18 #include <cstdint>
19 #include <utility>
20 #include <vector>
21 
22 #include "absl/base/attributes.h"
23 #include "absl/container/flat_hash_map.h"
24 #include "absl/container/flat_hash_set.h"
25 #include "ortools/sat/cp_model.pb.h"
29 #include "ortools/sat/sat_parameters.pb.h"
31 #include "ortools/util/bitset.h"
32 #include "ortools/util/logging.h"
35 
36 namespace operations_research {
37 namespace sat {
38 
39 // Replaces all the instance of a variable i (and the literals referring to it)
40 // by mapping[i]. The definition of variables i is also moved to its new index.
41 // Variables with a negative mapping value are ignored and it is an error if
42 // such variable is referenced anywhere (this is CHECKed).
43 //
44 // The image of the mapping should be dense in [0, new_num_variables), this is
45 // also CHECKed.
46 void ApplyVariableMapping(const std::vector<int>& mapping,
47  const PresolveContext& context);
48 
49 // Presolves the initial content of presolved_model.
50 //
51 // This also creates a mapping model that encode the correspondence between the
52 // two problems. This works as follow:
53 // - The first variables of mapping_model are in one to one correspondence with
54 // the variables of the initial model.
55 // - The presolved_model variables are in one to one correspondence with the
56 // variable at the indices given by postsolve_mapping in the mapping model.
57 // - Fixing one of the two sets of variables and solving the model will assign
58 // the other set to a feasible solution of the other problem. Moreover, the
59 // objective value of these solutions will be the same. Note that solving such
60 // problems will take little time in practice because the propagation will
61 // basically do all the work.
62 //
63 // Note(user): an optimization model can be transformed into a decision problem,
64 // if for instance the objective is fixed, or independent from the rest of the
65 // problem.
66 //
67 // TODO(user): Identify disconnected components and returns a vector of
68 // presolved model? If we go this route, it may be nicer to store the indices
69 // inside the model. We can add a IntegerVariableProto::initial_index;
71  public:
73  std::vector<int>* postsolve_mapping);
74 
75  // We returns the status of the problem after presolve:
76  // - UNKNOWN if everything was ok.
77  // - INFEASIBLE if the model was proven so during presolve
78  // - MODEL_INVALID if the model caused some issues, like if we are not able
79  // to scale a floating point objective with enough precision.
80  CpSolverStatus Presolve();
81 
82  // Executes presolve method for the given constraint. Public for testing only.
83  bool PresolveOneConstraint(int c);
84 
85  // Public for testing only.
87 
88  private:
89  // A simple helper that logs the rules applied so far and return INFEASIBLE.
90  CpSolverStatus InfeasibleStatus();
91 
92  // Runs the inner loop of the presolver.
93  void PresolveToFixPoint();
94 
95  // Runs the probing.
96  void Probe();
97 
98  // Presolve functions.
99  //
100  // They should return false only if the constraint <-> variable graph didn't
101  // change. This is just an optimization, returning true is always correct.
102  //
103  // Invariant about UNSAT: All these functions should abort right away if
104  // context_.IsUnsat() is true. And the only way to change the status to unsat
105  // is through ABSL_MUST_USE_RESULT function that should also abort right away
106  // the current code. This way we shouldn't keep doing computation on an
107  // inconsistent state.
108  // TODO(user): Make these public and unit test.
109  bool PresolveAllDiff(ConstraintProto* ct);
110  bool PresolveAutomaton(ConstraintProto* ct);
111  bool PresolveElement(ConstraintProto* ct);
112  bool PresolveIntAbs(ConstraintProto* ct);
113  bool PresolveIntDiv(ConstraintProto* ct);
114  bool PresolveIntMod(ConstraintProto* ct);
115  bool PresolveIntProd(ConstraintProto* ct);
116  bool PresolveInterval(int c, ConstraintProto* ct);
117  bool PresolveInverse(ConstraintProto* ct);
118  bool PresolveLinMax(ConstraintProto* ct);
119  bool PresolveLinMaxWhenAllBoolean(ConstraintProto* ct);
120  bool PresolveTable(ConstraintProto* ct);
121  void DetectDuplicateIntervals(
122  int c, google::protobuf::RepeatedField<int32_t>* intervals);
123  bool PresolveCumulative(ConstraintProto* ct);
124  bool PresolveNoOverlap(ConstraintProto* ct);
125  bool PresolveNoOverlap2D(int c, ConstraintProto* ct);
126  bool PresolveReservoir(ConstraintProto* ct);
127 
128  bool PresolveCircuit(ConstraintProto* ct);
129  bool PresolveRoutes(ConstraintProto* ct);
130 
131  bool PresolveAtMostOrExactlyOne(ConstraintProto* ct);
132  bool PresolveAtMostOne(ConstraintProto* ct);
133  bool PresolveExactlyOne(ConstraintProto* ct);
134 
135  bool PresolveBoolAnd(ConstraintProto* ct);
136  bool PresolveBoolOr(ConstraintProto* ct);
137  bool PresolveBoolXor(ConstraintProto* ct);
138  bool PresolveEnforcementLiteral(ConstraintProto* ct);
139 
140  // Regroups terms and substitute affine relations.
141  // Returns true if the set of variables in the expression changed.
142  template <typename ProtoWithVarsAndCoeffs>
143  bool CanonicalizeLinearExpressionInternal(const ConstraintProto& ct,
144  ProtoWithVarsAndCoeffs* proto,
145  int64_t* offset);
146  bool CanonicalizeLinearExpression(const ConstraintProto& ct,
147  LinearExpressionProto* exp);
148  bool CanonicalizeLinearArgument(const ConstraintProto& ct,
149  LinearArgumentProto* proto);
150 
151  // For the linear constraints, we have more than one function.
152  bool CanonicalizeLinear(ConstraintProto* ct);
153  bool PropagateDomainsInLinear(int ct_index, ConstraintProto* ct);
154  bool RemoveSingletonInLinear(ConstraintProto* ct);
155  bool PresolveSmallLinear(ConstraintProto* ct);
156  bool PresolveLinearOfSizeOne(ConstraintProto* ct);
157  bool PresolveLinearOfSizeTwo(ConstraintProto* ct);
158  bool PresolveLinearOnBooleans(ConstraintProto* ct);
159  bool PresolveDiophantine(ConstraintProto* ct);
160  bool AddVarAffineRepresentativeFromLinearEquality(int target_index,
161  ConstraintProto* ct);
162  bool PresolveLinearEqualityWithModulo(ConstraintProto* ct);
163 
164  // It can be interesting to know for a given linear constraint that a subset
165  // of its variables are in at most one relation.
166  void DetectAndProcessAtMostOneInLinear(int ct_index, ConstraintProto* ct,
167  ActivityBoundHelper* helper);
168 
169  // If a constraint is of the form "a * expr_X + expr_Y" and expr_Y can only
170  // take small values compared to a, depending on the bounds, the constraint
171  // can be equivalent to a constraint on expr_X only.
172  //
173  // For instance "10'001 X + 9'999 Y <= 105'000, with X, Y in [0, 100]" can
174  // be rewritten as X + Y <= 10 ! This can easily happen after scaling to
175  // integer cofficient a floating point constraint.
176  void TryToReduceCoefficientsOfLinearConstraint(int c, ConstraintProto* ct);
177 
178  // This detects and converts constraints of the form:
179  // "X = sum Boolean * value", with "sum Boolean <= 1".
180  //
181  // Note that it is not super fast, so it shouldn't be called too often.
182  void ExtractEncodingFromLinear();
183  bool ProcessEncodingFromLinear(int linear_encoding_ct_index,
184  const ConstraintProto& at_most_or_exactly_one,
185  int64_t* num_unique_terms,
186  int64_t* num_multiple_terms);
187 
188  // Remove duplicate constraints. This also merge domain of linear constraints
189  // with duplicate linear expressions.
190  void DetectDuplicateConstraints();
191 
192  // Detects if a linear constraint is "included" in another one, and do
193  // related presolve.
194  void DetectDominatedLinearConstraints();
195 
196  // SetPPC is short for set packing, partitioning and covering constraints.
197  // These are sum of booleans <=, = and >= 1 respectively.
198  // We detect inclusion of these constraint which allows a few simplifications.
199  void ProcessSetPPC();
200 
201  // Detect if one constraints has a subset of enforcement of another.
202  void DetectIncludedEnforcement();
203 
204  // Removes dominated constraints or fixes some variables for given pair of
205  // setppc constraints included in each other.
206  bool ProcessSetPPCSubset(int subset_c, int superset_c,
207  absl::flat_hash_set<int>* tmp_set,
208  bool* remove_subset, bool* remove_superset,
209  bool* stop_processing_superset);
210 
211  // Run SAT specific presolve code.
212  void PresolvePureSatPart();
213 
214  // Extracts AtMostOne constraint from Linear constraint.
215  void ExtractAtMostOneFromLinear(ConstraintProto* ct);
216 
217  // Returns true if the constraint changed.
218  bool DivideLinearByGcd(ConstraintProto* ct);
219 
220  void ExtractEnforcementLiteralFromLinearConstraint(int ct_index,
221  ConstraintProto* ct);
222  void LowerThanCoeffStrengthening(bool from_lower_bound, int64_t min_magnitude,
223  int64_t threshold, ConstraintProto* ct);
224 
225  // Extracts cliques from bool_and and small at_most_one constraints and
226  // transforms them into maximal cliques.
227  void TransformIntoMaxCliques();
228 
229  // Converts bool_or and at_most_one of size 2 to bool_and.
230  void ExtractBoolAnd();
231 
232  // Try to reformulate the objective in term of "base" variables. This is
233  // mainly useful for core based approach where having more terms in the
234  // objective (but with a same trivial lower bound) should help.
235  void ExpandObjective();
236 
237  // This makes a big difference on the flatzinc mznc2017_aes_opt* problems.
238  // Where, with this, the core based approach can find small cores and close
239  // them quickly.
240  //
241  // TODO(user): Is it by chance or there is a underlying deep reason? try to
242  // merge this with what ExpandObjective() is doing.
243  void ShiftObjectiveWithExactlyOnes();
244 
245  void ProcessVariableOnlyUsedInEncoding(int var);
246  void TryToSimplifyDomain(int var);
247 
248  void LookAtVariableWithDegreeTwo(int var);
249  void ProcessVariableInTwoAtMostOrExactlyOne(int var);
250 
251  void MergeNoOverlapConstraints();
252 
253  // Try to identify many linear constraints that share a common linear
254  // expression.
255  void FindBigLinearOverlap();
256 
257  // Heuristic to merge clauses that differ in only one literal.
258  // The idea is to regroup a bunch of clauses into a single bool_and.
259  // This serves a bunch of purpose:
260  // - Smaller model.
261  // - Stronger dual reasoning since less locks.
262  // - If the negation of the rhs of the bool_and are in at most one, we will
263  // have a stronger LP relaxation.
264  //
265  // TODO(user): If the merge procedure is successful we might want to develop
266  // a custom propagators for such bool_and. It should in theory be more
267  // efficient than the two watcher literal scheme for clauses. Investigate!
268  void MergeClauses();
269 
270  // Boths function are responsible for dealing with affine relations.
271  // The second one returns false on UNSAT.
272  void EncodeAllAffineRelations();
273  bool PresolveAffineRelationIfAny(int var);
274 
275  bool ExploitEquivalenceRelations(int c, ConstraintProto* ct);
276 
277  ABSL_MUST_USE_RESULT bool RemoveConstraint(ConstraintProto* ct);
278  ABSL_MUST_USE_RESULT bool MarkConstraintAsFalse(ConstraintProto* ct);
279 
280  std::vector<int>* postsolve_mapping_;
281  PresolveContext* context_;
282  SolverLogger* logger_;
283 
284  // Used by CanonicalizeLinearExpressionInternal().
285  std::vector<std::pair<int, int64_t>> tmp_terms_;
286 
287  // Used by DetectAndProcessAtMostOneInLinear().
288  std::vector<std::array<int64_t, 2>> conditional_mins_;
289  std::vector<std::array<int64_t, 2>> conditional_maxs_;
290 
291  // Used by ProcessSetPPCSubset() and DetectAndProcessAtMostOneInLinear() to
292  // propagate linear with an at_most_one or exactly_one included inside.
293  absl::flat_hash_map<int, int> temp_map_;
294  absl::flat_hash_set<int> temp_set_;
295  ConstraintProto temp_ct_;
296 
297  // Use by TryToReduceCoefficientsOfLinearConstraint().
298  MaxBoundedSubsetSum lb_feasible_;
299  MaxBoundedSubsetSum lb_infeasible_;
300  MaxBoundedSubsetSum ub_feasible_;
301  MaxBoundedSubsetSum ub_infeasible_;
302 };
303 
304 // This helper class perform copy with simplification from a model and a
305 // partial assignment to another model. The purpose is to miminize the size of
306 // the copied model, as well as to reduce the pressure on the memory sub-system.
307 //
308 // It is currently used by the LNS part, but could be used with any other scheme
309 // that generates partial assignments.
310 class ModelCopy {
311  public:
312  explicit ModelCopy(PresolveContext* context);
313 
314  // Copies all constraints from in_model to working model of the context.
315  //
316  // During the process, it will read variable domains from the context, and
317  // simplify constraints to minimize the size of the copied model.
318  // Thus it is important that the context->working_model already have the
319  // variables part copied.
320  //
321  // It returns false iff the model is proven infeasible.
322  //
323  // It does not clear the constraints part of the working model of the context.
324  //
325  // Note(user): If first_copy is true, we will reorder the scheduling
326  // constraint so that they only use reference to previously defined intervals.
327  // This allow to be more efficient later in a few preprocessing steps.
328  bool ImportAndSimplifyConstraints(const CpModelProto& in_model,
329  const std::vector<int>& ignored_constraints,
330  bool first_copy = false);
331 
332  // Copy variables from the in_model to the working model.
333  // It reads the 'ignore_names' parameters from the context, and keeps or
334  // deletes names accordingly.
335  void ImportVariablesAndMaybeIgnoreNames(const CpModelProto& in_model);
336 
337  private:
338  // Overwrites the out_model to be unsat. Returns false.
339  bool CreateUnsatModel();
340 
341  void CopyEnforcementLiterals(const ConstraintProto& orig,
342  ConstraintProto* dest);
343  bool OneEnforcementLiteralIsFalse(const ConstraintProto& ct) const;
344 
345  // All these functions return false if the constraint is found infeasible.
346  bool CopyBoolOr(const ConstraintProto& ct);
347  bool CopyBoolOrWithDupSupport(const ConstraintProto& ct);
348  bool CopyBoolAnd(const ConstraintProto& ct);
349  bool CopyLinear(const ConstraintProto& ct);
350  bool CopyAtMostOne(const ConstraintProto& ct);
351  bool CopyExactlyOne(const ConstraintProto& ct);
352  bool CopyInterval(const ConstraintProto& ct, int c, bool ignore_names);
353 
354  // These function remove unperformed intervals. Note that they requires
355  // interval to appear before (validated) as they test unperformed by testing
356  // if interval_mapping_ is empty.
357  void CopyAndMapNoOverlap(const ConstraintProto& ct);
358  void CopyAndMapNoOverlap2D(const ConstraintProto& ct);
359  void CopyAndMapCumulative(const ConstraintProto& ct);
360 
361  PresolveContext* context_;
362  int64_t skipped_non_zero_ = 0;
363 
364  // Temp vectors.
365  std::vector<int> non_fixed_variables_;
366  std::vector<int64_t> non_fixed_coefficients_;
367  absl::flat_hash_map<int, int> interval_mapping_;
368  int starting_constraint_index_ = 0;
369  std::vector<int> temp_enforcement_literals_;
370 
371  std::vector<int> temp_literals_;
372  absl::flat_hash_set<int> tmp_literals_set_;
373 };
374 
375 // Copy in_model to the model in the presolve context.
376 // It performs on the fly simplification, and returns false if the
377 // model is proved infeasible. If reads the parameters 'ignore_names' and keeps
378 // or deletes variables and constraints names accordingly.
379 //
380 // This should only be called on the first copy of the user given model.
381 // Note that this reorder all constraints that use intervals last. We loose the
382 // user-defined order, but hopefully that should not matter too much.
383 bool ImportModelWithBasicPresolveIntoContext(const CpModelProto& in_model,
385 
386 // Copies the non constraint, non variables part of the model.
388  const CpModelProto& in_model, PresolveContext* context);
389 
390 // Convenient wrapper to call the full presolve.
391 CpSolverStatus PresolveCpModel(PresolveContext* context,
392  std::vector<int>* postsolve_mapping);
393 
394 // Returns the index of duplicate constraints in the given proto in the first
395 // element of each pair. The second element of each pair is the "representative"
396 // that is the first constraint in the proto in a set of duplicate constraints.
397 //
398 // Empty constraints are ignored. We also do a bit more:
399 // - We ignore names when comparing constraint.
400 // - For linear constraints, we ignore the domain. This is because we can
401 // just merge them if the constraints are the same.
402 // - We return the special kObjectiveConstraint (< 0) representative if a linear
403 // constraint is parallel to the objective and has no enforcement literals.
404 // The domain of such constraint can just be merged with the objective domain.
405 //
406 // If ignore_enforcement is true, we ignore enforcement literal, but do not
407 // do the linear domain or objective special cases. This allow to cover some
408 // other cases like:
409 // - enforced constraint duplicate of non-enforced one.
410 // - Two enforced constraints with singleton enforcement (vpphard).
411 //
412 // Visible here for testing. This is meant to be called at the end of the
413 // presolve where constraints have been canonicalized.
414 std::vector<std::pair<int, int>> FindDuplicateConstraints(
415  const CpModelProto& model_proto, bool ignore_enforcement = false);
416 
417 } // namespace sat
418 } // namespace operations_research
419 
420 #endif // OR_TOOLS_SAT_CP_MODEL_PRESOLVE_H_
CpModelPresolver(PresolveContext *context, std::vector< int > *postsolve_mapping)
bool ImportAndSimplifyConstraints(const CpModelProto &in_model, const std::vector< int > &ignored_constraints, bool first_copy=false)
void ImportVariablesAndMaybeIgnoreNames(const CpModelProto &in_model)
CpModelProto proto
CpModelProto const * model_proto
const Constraint * ct
IntVar * var
Definition: expr_array.cc:1874
GurobiMPCallbackContext * context
std::vector< std::pair< int, int > > FindDuplicateConstraints(const CpModelProto &model_proto, bool ignore_enforcement)
void CopyEverythingExceptVariablesAndConstraintsFieldsIntoContext(const CpModelProto &in_model, PresolveContext *context)
CpSolverStatus PresolveCpModel(PresolveContext *context, std::vector< int > *postsolve_mapping)
bool ImportModelWithBasicPresolveIntoContext(const CpModelProto &in_model, PresolveContext *context)
void ApplyVariableMapping(const std::vector< int > &mapping, const PresolveContext &context)
Collection of objects used to extend the Constraint Solver library.