OR-Tools  9.3
cp_model_presolve.h
Go to the documentation of this file.
1// Copyright 2010-2021 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 <cstdint>
18#include <utility>
19#include <vector>
20
21#include "absl/base/attributes.h"
22#include "absl/container/flat_hash_map.h"
23#include "absl/container/flat_hash_set.h"
24#include "ortools/sat/cp_model.pb.h"
28#include "ortools/sat/sat_parameters.pb.h"
30#include "ortools/util/bitset.h"
34
35namespace operations_research {
36namespace sat {
37
38// Replaces all the instance of a variable i (and the literals referring to it)
39// by mapping[i]. The definition of variables i is also moved to its new index.
40// Variables with a negative mapping value are ignored and it is an error if
41// such variable is referenced anywhere (this is CHECKed).
42//
43// The image of the mapping should be dense in [0, new_num_variables), this is
44// also CHECKed.
45void ApplyVariableMapping(const std::vector<int>& mapping,
46 const PresolveContext& context);
47
48// Presolves the initial content of presolved_model.
49//
50// This also creates a mapping model that encode the correspondence between the
51// two problems. This works as follow:
52// - The first variables of mapping_model are in one to one correspondence with
53// the variables of the initial model.
54// - The presolved_model variables are in one to one correspondence with the
55// variable at the indices given by postsolve_mapping in the mapping model.
56// - Fixing one of the two sets of variables and solving the model will assign
57// the other set to a feasible solution of the other problem. Moreover, the
58// objective value of these solutions will be the same. Note that solving such
59// problems will take little time in practice because the propagation will
60// basically do all the work.
61//
62// Note(user): an optimization model can be transformed into a decision problem,
63// if for instance the objective is fixed, or independent from the rest of the
64// problem.
65//
66// TODO(user): Identify disconnected components and returns a vector of
67// presolved model? If we go this route, it may be nicer to store the indices
68// inside the model. We can add a IntegerVariableProto::initial_index;
70 public:
72 std::vector<int>* postsolve_mapping);
73
74 // We returns the status of the problem after presolve:
75 // - UNKNOWN if everything was ok.
76 // - INFEASIBLE if the model was proven so during presolve
77 // - MODEL_INVALID if the model caused some issues, like if we are not able
78 // to scale a floating point objective with enough precision.
79 CpSolverStatus Presolve();
80
81 // Executes presolve method for the given constraint. Public for testing only.
82 bool PresolveOneConstraint(int c);
83
84 // Public for testing only.
86
87 private:
88 // A simple helper that logs the rules applied so far and return INFEASIBLE.
89 CpSolverStatus InfeasibleStatus();
90
91 // Runs the inner loop of the presolver.
92 void PresolveToFixPoint();
93
94 // Runs the probing.
95 void Probe();
96
97 // Presolve functions.
98 //
99 // They should return false only if the constraint <-> variable graph didn't
100 // change. This is just an optimization, returning true is always correct.
101 //
102 // Invariant about UNSAT: All these functions should abort right away if
103 // context_.IsUnsat() is true. And the only way to change the status to unsat
104 // is through ABSL_MUST_USE_RESULT function that should also abort right away
105 // the current code. This way we shouldn't keep doing computation on an
106 // inconsistent state.
107 // TODO(user): Make these public and unit test.
108 bool PresolveAllDiff(ConstraintProto* ct);
109 bool PresolveAutomaton(ConstraintProto* ct);
110 bool PresolveElement(ConstraintProto* ct);
111 bool PresolveIntAbs(ConstraintProto* ct);
112 bool PresolveIntDiv(ConstraintProto* ct);
113 bool PresolveIntMod(ConstraintProto* ct);
114 bool PresolveIntProd(ConstraintProto* ct);
115 bool PresolveInterval(int c, ConstraintProto* ct);
116 bool PresolveInverse(ConstraintProto* ct);
117 bool PresolveLinMax(ConstraintProto* ct);
118 bool PresolveTable(ConstraintProto* ct);
119
120 bool PresolveCumulative(ConstraintProto* ct);
121 bool PresolveNoOverlap(ConstraintProto* ct);
122 bool PresolveNoOverlap2D(int c, ConstraintProto* ct);
123 bool PresolveReservoir(ConstraintProto* ct);
124
125 bool PresolveCircuit(ConstraintProto* ct);
126 bool PresolveRoutes(ConstraintProto* ct);
127
128 bool PresolveAtMostOrExactlyOne(ConstraintProto* ct);
129 bool PresolveAtMostOne(ConstraintProto* ct);
130 bool PresolveExactlyOne(ConstraintProto* ct);
131
132 bool PresolveBoolAnd(ConstraintProto* ct);
133 bool PresolveBoolOr(ConstraintProto* ct);
134 bool PresolveBoolXor(ConstraintProto* ct);
135 bool PresolveEnforcementLiteral(ConstraintProto* ct);
136
137 // Regroups terms and substitute affine relations.
138 // Returns true if the set of variables in the expression changed.
139 template <typename ProtoWithVarsAndCoeffs>
140 bool CanonicalizeLinearExpressionInternal(const ConstraintProto& ct,
141 ProtoWithVarsAndCoeffs* proto,
142 int64_t* offset);
143 bool CanonicalizeLinearExpression(const ConstraintProto& ct,
144 LinearExpressionProto* exp);
145 bool CanonicalizeLinearArgument(const ConstraintProto& ct,
146 LinearArgumentProto* proto);
147
148 // For the linear constraints, we have more than one function.
149 bool CanonicalizeLinear(ConstraintProto* ct);
150 bool PropagateDomainsInLinear(int c, ConstraintProto* ct);
151 bool RemoveSingletonInLinear(ConstraintProto* ct);
152 bool PresolveSmallLinear(ConstraintProto* ct);
153 bool PresolveLinearOfSizeOne(ConstraintProto* ct);
154 bool PresolveLinearOfSizeTwo(ConstraintProto* ct);
155 bool PresolveLinearOnBooleans(ConstraintProto* ct);
156 bool AddVarAffineRepresentativeFromLinearEquality(int target_index,
157 ConstraintProto* ct);
158 bool PresolveLinearEqualityWithModulo(ConstraintProto* ct);
159 bool DetectAndProcessOneSidedLinearConstraint(int c, ConstraintProto* ct);
160
161 // If a constraint is of the form "a * expr_X + expr_Y" and expr_Y can only
162 // take small values compared to a, depending on the bounds, the constraint
163 // can be equivalent to a constraint on expr_X only.
164 //
165 // For instance "10'001 X + 9'999 Y <= 105'000, with X, Y in [0, 100]" can
166 // be rewritten as X + Y <= 10 ! This can easily happen after scaling to
167 // integer cofficient a floating point constraint.
168 void TryToReduceCoefficientsOfLinearConstraint(int c, ConstraintProto* ct);
169
170 // This detects and converts constraints of the form:
171 // "X = sum Boolean * value", with "sum Boolean <= 1".
172 //
173 // Note that it is not super fast, so it shouldn't be called too often.
174 void ExtractEncodingFromLinear();
175 bool ProcessEncodingFromLinear(int linear_encoding_ct_index,
176 const ConstraintProto& at_most_or_exactly_one,
177 int64_t* num_unique_terms,
178 int64_t* num_multiple_terms);
179
180 // Remove duplicate constraints. This also merge domain of linear constraints
181 // with duplicate linear expressions.
182 void DetectDuplicateConstraints();
183
184 // Detects if a linear constraint is "included" in another one, and do
185 // related presolve.
186 void DetectDominatedLinearConstraints();
187
188 // Returns true if the domain of x is implied by the current constraints.
189 //
190 // Warning: this potentially scan all the constraint in which x appear.
191 // work_done will be incremented by the number of entries scanned.
192 bool IsImpliedFree(const std::vector<int>& constraints, int x,
193 int64_t* work_done);
194
195 // Detects if two columns that contains only linear constraints share a lot of
196 // common entries. Then calls PerformFreeColumnSubstitution() on promising
197 // candidate to reduce the overall number of non zeros.
198 void DetectOverlappingColumns();
199
200 // Assuming that x is implied free and only appear in linear constraints, this
201 // replace x by x + factor * y. This assumes that we precomputed all the
202 // constraints in which x appears and the coefficient of x inside.
203 void PerformFreeColumnSubstitution(
204 const std::vector<std::pair<int, int64_t>>& constraints_with_x_coeff,
205 int x, int y, int64_t factor);
206
207 // SetPPC is short for set packing, partitioning and covering constraints.
208 // These are sum of booleans <=, = and >= 1 respectively.
209 // We detect inclusion of these constraint which allows a few simplifications.
210 void ProcessSetPPC();
211
212 // Removes dominated constraints or fixes some variables for given pair of
213 // setppc constraints included in each other.
214 bool ProcessSetPPCSubset(int subset_c, int superset_c,
215 absl::flat_hash_set<int>* tmp_set,
216 bool* remove_subset, bool* remove_superset);
217
218 // Run SAT specific presolve code.
219 void PresolvePureSatPart();
220
221 // Extracts AtMostOne constraint from Linear constraint.
222 void ExtractAtMostOneFromLinear(ConstraintProto* ct);
223
224 // Returns true if the constraint changed.
225 bool DivideLinearByGcd(ConstraintProto* ct);
226
227 void ExtractEnforcementLiteralFromLinearConstraint(int ct_index,
228 ConstraintProto* ct);
229
230 // Extracts cliques from bool_and and small at_most_one constraints and
231 // transforms them into maximal cliques.
232 void TransformIntoMaxCliques();
233
234 // Converts bool_or and at_most_one of size 2 to bool_and.
235 void ExtractBoolAnd();
236
237 void ExpandObjective();
238
239 void LookAtVariableWithDegreeTwo(int var);
240 void ProcessVariableOnlyUsedInEncoding(int var);
241 void TryToSimplifyDomain(int var);
242
243 void MergeNoOverlapConstraints();
244
245 // Boths function are responsible for dealing with affine relations.
246 // The second one returns false on UNSAT.
247 void EncodeAllAffineRelations();
248 bool PresolveAffineRelationIfAny(int var);
249
250 bool ExploitEquivalenceRelations(int c, ConstraintProto* ct);
251
252 ABSL_MUST_USE_RESULT bool RemoveConstraint(ConstraintProto* ct);
253 ABSL_MUST_USE_RESULT bool MarkConstraintAsFalse(ConstraintProto* ct);
254
255 std::vector<int>* postsolve_mapping_;
256 PresolveContext* context_;
257 SolverLogger* logger_;
258
259 // Used by CanonicalizeLinearExpressionInternal().
260 std::vector<std::pair<int, int64_t>> tmp_terms_;
261};
262
263// This helper class perform copy with simplification from a model and a
264// partial assignment to another model. The purpose is to miminize the size of
265// the copied model, as well as to reduce the pressure on the memory sub-system.
266//
267// It is currently used by the LNS part, but could be used with any other scheme
268// that generates partial assignments.
270 public:
272
273 // Copies all constraints from in_model to working model of the context.
274 //
275 // During the process, it will read variable domains from the context, and
276 // simplify constraints to minimize the size of the copied model.
277 // Thus it is important that the context->working_model already have the
278 // variables part copied.
279 //
280 // It returns false iff the model is proven infeasible.
281 //
282 // It does not clear the constraints part of the working model of the context.
283 //
284 // Note(user): If first_copy is true, we will reorder the scheduling
285 // constraint so that they only use reference to previously defined intervals.
286 // This allow to be more efficient later in a few preprocessing steps.
287 bool ImportAndSimplifyConstraints(const CpModelProto& in_model,
288 const std::vector<int>& ignored_constraints,
289 bool first_copy = false);
290
291 // Copy variables from the in_model to the working model.
292 // It reads the 'ignore_names' parameters from the context, and keeps or
293 // deletes names accordingly.
294 void ImportVariablesAndMaybeIgnoreNames(const CpModelProto& in_model);
295
296 private:
297 // Overwrites the out_model to be unsat. Returns false.
298 bool CreateUnsatModel();
299
300 void CopyEnforcementLiterals(const ConstraintProto& orig,
301 ConstraintProto* dest);
302 bool OneEnforcementLiteralIsFalse(const ConstraintProto& ct) const;
303
304 // All these functions return false if the constraint is found infeasible.
305 bool CopyBoolOr(const ConstraintProto& ct);
306 bool CopyBoolOrWithDupSupport(const ConstraintProto& ct);
307 bool CopyBoolAnd(const ConstraintProto& ct);
308 bool CopyLinear(const ConstraintProto& ct);
309 bool CopyAtMostOne(const ConstraintProto& ct);
310 bool CopyExactlyOne(const ConstraintProto& ct);
311 bool CopyInterval(const ConstraintProto& ct, int c, bool ignore_names);
312
313 // These function remove unperformed intervals. Note that they requires
314 // interval to appear before (validated) as they test unperformed by testing
315 // if interval_mapping_ is empty.
316 void CopyAndMapNoOverlap(const ConstraintProto& ct);
317 void CopyAndMapNoOverlap2D(const ConstraintProto& ct);
318 void CopyAndMapCumulative(const ConstraintProto& ct);
319
320 PresolveContext* context_;
321 int64_t skipped_non_zero_ = 0;
322
323 // Temp vectors.
324 std::vector<int> non_fixed_variables_;
325 std::vector<int64_t> non_fixed_coefficients_;
326 absl::flat_hash_map<int, int> interval_mapping_;
327 int starting_constraint_index_ = 0;
328 std::vector<int> temp_enforcement_literals_;
329
330 std::vector<int> temp_literals_;
331 absl::flat_hash_set<int> tmp_literals_set_;
332};
333
334// Copy in_model to the model in the presolve context.
335// It performs on the fly simplification, and returns false if the
336// model is proved infeasible. If reads the parameters 'ignore_names' and keeps
337// or deletes variables and constraints names accordingly.
338//
339// This should only be called on the first copy of the user given model.
340// Note that this reorder all constraints that use intervals last. We loose the
341// user-defined order, but hopefully that should not matter too much.
342bool ImportModelWithBasicPresolveIntoContext(const CpModelProto& in_model,
344
345// Copies the non constraint, non variables part of the model.
347 const CpModelProto& in_model, PresolveContext* context);
348
349// Convenient wrapper to call the full presolve.
351 std::vector<int>* postsolve_mapping);
352
353// Returns the index of duplicate constraints in the given proto in the first
354// element of each pair. The second element of each pair is the "representative"
355// that is the first constraint in the proto in a set of duplicate constraints.
356//
357// Empty constraints are ignored. We also do a bit more:
358// - We ignore names when comparing constraint.
359// - For linear constraints, we ignore the domain. This is because we can
360// just merge them if the constraints are the same.
361// - We return the special kObjectiveConstraint (< 0) representative if a linear
362// constraint is parallel to the objective and has no enforcement literals.
363// The domain of such constraint can just be merged with the objective domain.
364//
365// Visible here for testing. This is meant to be called at the end of the
366// presolve where constraints have been canonicalized.
367std::vector<std::pair<int, int>> FindDuplicateConstraints(
368 const CpModelProto& model_proto);
369
370} // namespace sat
371} // namespace operations_research
372
373#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
void CopyEverythingExceptVariablesAndConstraintsFieldsIntoContext(const CpModelProto &in_model, PresolveContext *context)
std::vector< std::pair< int, int > > FindDuplicateConstraints(const CpModelProto &model_proto)
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.