Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
presolve_context.h
Go to the documentation of this file.
1// Copyright 2010-2025 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_PRESOLVE_CONTEXT_H_
15#define OR_TOOLS_SAT_PRESOLVE_CONTEXT_H_
16
17#include <cstdint>
18#include <optional>
19#include <string>
20#include <tuple>
21#include <utility>
22#include <vector>
23
24#include "absl/base/attributes.h"
25#include "absl/container/flat_hash_map.h"
26#include "absl/container/flat_hash_set.h"
27#include "absl/flags/declare.h"
28#include "absl/log/check.h"
29#include "absl/strings/str_cat.h"
30#include "absl/strings/string_view.h"
31#include "absl/types/span.h"
33#include "ortools/sat/cp_model.pb.h"
35#include "ortools/sat/model.h"
37#include "ortools/sat/sat_parameters.pb.h"
39#include "ortools/sat/util.h"
41#include "ortools/util/bitset.h"
46
47ABSL_DECLARE_FLAG(bool, cp_model_debug_postsolve);
48
49namespace operations_research {
50namespace sat {
51
52// We use some special constraint index in our variable <-> constraint graph.
53constexpr int kObjectiveConstraint = -1;
54constexpr int kAffineRelationConstraint = -2;
55constexpr int kAssumptionsConstraint = -3;
56
57class PresolveContext;
58
59// When storing a reference to a literal, it is important not to forget when
60// reading it back to take its representative. Otherwise, we might introduce
61// literal that have already been removed, which will break invariants in a
62// bunch of places.
64 public:
65 SavedLiteral() = default;
66 explicit SavedLiteral(int ref) : ref_(ref) {}
67 int Get(PresolveContext* context) const;
68
69 private:
70 int ref_ = 0;
71};
72
73// Same as SavedLiteral for variable.
74//
75// TODO(user): get rid of this, we don't have the notion of equivalent variable
76// anymore, but the more general affine relation one. We just need to support
77// general affine for the linear1 involving an absolute value.
79 public:
80 SavedVariable() = default;
81 explicit SavedVariable(int ref) : ref_(ref) {}
82 int Get() const;
83
84 private:
85 int ref_ = 0;
86};
87
88// If a floating point objective is present, scale it using the current domains
89// and transform it to an integer_objective.
90ABSL_MUST_USE_RESULT bool ScaleFloatingPointObjective(
91 const SatParameters& params, SolverLogger* logger, CpModelProto* proto);
92
93// Wrap the CpModelProto we are presolving with extra data structure like the
94// in-memory domain of each variables and the constraint variable graph.
96 public:
97 PresolveContext(Model* model, CpModelProto* cp_model, CpModelProto* mapping)
98 : working_model(cp_model),
99 mapping_model(mapping),
100 logger_(model->GetOrCreate<SolverLogger>()),
101 params_(*model->GetOrCreate<SatParameters>()),
102 time_limit_(model->GetOrCreate<TimeLimit>()),
103 random_(model->GetOrCreate<ModelRandomGenerator>()) {}
104
105 // Helpers to adds new variables to the presolved model.
106
107 // Creates a new integer variable with the given domain.
108 // WARNING: this does not set any hint value for the new variable.
109 int NewIntVar(const Domain& domain);
110
111 // Creates a new Boolean variable.
112 // WARNING: this does not set any hint value for the new variable.
113 int NewBoolVar(absl::string_view source);
114
115 // Creates a new integer variable with the given domain and definition.
116 // By default this also creates the linking constraint new_var = definition.
117 // Its hint value is set to the value of the definition. Returns -1 if we
118 // couldn't create the definition due to overflow.
120 const Domain& domain,
121 absl::Span<const std::pair<int, int64_t>> definition,
122 bool append_constraint_to_mapping_model = false);
123
124 // Creates a new bool var.
125 // Its hint value is set to the value of the given clause.
126 int NewBoolVarWithClause(absl::Span<const int> clause);
127
128 // Creates a new bool var.
129 // Its hint value is set to the value of the given conjunction.
130 int NewBoolVarWithConjunction(absl::Span<const int> conjunction);
131
132 // Some expansion code use constant literal to be simpler to write. This will
133 // create a NewBoolVar() the first time, but later call will just returns it.
134 int GetTrueLiteral();
135 int GetFalseLiteral();
136
137 // a => b.
138 void AddImplication(int a, int b);
139
140 // b => (x ∈ domain).
141 void AddImplyInDomain(int b, int x, const Domain& domain);
142
143 // b => (expr ∈ domain).
144 void AddImplyInDomain(int b, const LinearExpressionProto& expr,
145 const Domain& domain);
146
147 // Helpers to query the current domain of a variable.
148 bool DomainIsEmpty(int ref) const;
149 bool IsFixed(int ref) const;
150 bool CanBeUsedAsLiteral(int ref) const;
151 bool LiteralIsTrue(int lit) const;
152 bool LiteralIsFalse(int lit) const;
153 int64_t MinOf(int ref) const;
154 int64_t MaxOf(int ref) const;
155 int64_t FixedValue(int ref) const;
156 bool DomainContains(int ref, int64_t value) const;
157 Domain DomainOf(int ref) const;
158 absl::Span<const Domain> AllDomains() const { return domains_; }
159
160 // Helper to query the state of an interval.
161 bool IntervalIsConstant(int ct_ref) const;
162 int64_t StartMin(int ct_ref) const;
163 int64_t StartMax(int ct_ref) const;
164 int64_t SizeMin(int ct_ref) const;
165 int64_t SizeMax(int ct_ref) const;
166 int64_t EndMin(int ct_ref) const;
167 int64_t EndMax(int ct_ref) const;
168 std::string IntervalDebugString(int ct_ref) const;
169
170 // Helpers to query the current domain of a linear expression.
171 // This doesn't check for integer overflow, but our linear expression
172 // should be such that this cannot happen (tested at validation).
173 int64_t MinOf(const LinearExpressionProto& expr) const;
174 int64_t MaxOf(const LinearExpressionProto& expr) const;
175 bool IsFixed(const LinearExpressionProto& expr) const;
176 int64_t FixedValue(const LinearExpressionProto& expr) const;
177
178 // This is faster than testing IsFixed() + FixedValue().
179 std::optional<int64_t> FixedValueOrNullopt(
180 const LinearExpressionProto& expr) const;
181
182 // Accepts any proto with two parallel vector .vars() and .coeffs(), like
183 // LinearConstraintProto or ObjectiveProto or LinearExpressionProto but beware
184 // that this ignore any offset.
185 template <typename ProtoWithVarsAndCoeffs>
186 std::pair<int64_t, int64_t> ComputeMinMaxActivity(
187 const ProtoWithVarsAndCoeffs& proto) const {
188 int64_t min_activity = 0;
189 int64_t max_activity = 0;
190 const int num_vars = proto.vars().size();
191 for (int i = 0; i < num_vars; ++i) {
192 const int var = proto.vars(i);
193 const int64_t coeff = proto.coeffs(i);
194 if (coeff > 0) {
195 min_activity += coeff * MinOf(var);
196 max_activity += coeff * MaxOf(var);
197 } else {
198 min_activity += coeff * MaxOf(var);
199 max_activity += coeff * MinOf(var);
200 }
201 }
202 return {min_activity, max_activity};
203 }
204
205 // Utility function.
206 void CappedUpdateMinMaxActivity(int var, int64_t coeff, int64_t* min_activity,
207 int64_t* max_activity) {
208 if (coeff > 0) {
209 *min_activity = CapAdd(*min_activity, CapProd(coeff, MinOf(var)));
210 *max_activity = CapAdd(*max_activity, CapProd(coeff, MaxOf(var)));
211 } else {
212 *min_activity = CapAdd(*min_activity, CapProd(coeff, MaxOf(var)));
213 *max_activity = CapAdd(*max_activity, CapProd(coeff, MinOf(var)));
214 }
215 }
216
217 // Canonicalization of linear constraint. This might also be needed when
218 // creating new constraint to make sure there are no duplicate variables.
219 // Returns true if the set of variables in the expression changed.
220 //
221 // This uses affine relation and regroup duplicate/fixed terms.
222 bool CanonicalizeLinearConstraint(ConstraintProto* ct);
223 bool CanonicalizeLinearExpression(absl::Span<const int> enforcements,
224 LinearExpressionProto* expr);
225
226 // This methods only works for affine expressions (checked).
227 bool DomainContains(const LinearExpressionProto& expr, int64_t value) const;
228
229 // Return a super-set of the domain of the linear expression.
230 Domain DomainSuperSetOf(const LinearExpressionProto& expr) const;
231
232 // Returns true iff the expr is of the form a * literal + b.
233 // The other function can be used to get the literal that achieve MaxOf().
234 bool ExpressionIsAffineBoolean(const LinearExpressionProto& expr) const;
235 int LiteralForExpressionMax(const LinearExpressionProto& expr) const;
236
237 // Returns true iff the expr is of the form 1 * var + 0.
238 bool ExpressionIsSingleVariable(const LinearExpressionProto& expr) const;
239
240 // Returns true iff the expr is a literal (x or not(x)).
241 bool ExpressionIsALiteral(const LinearExpressionProto& expr,
242 int* literal = nullptr) const;
243
244 // This function takes a positive variable reference.
245 bool DomainOfVarIsIncludedIn(int var, const Domain& domain) {
246 return domains_[var].IsIncludedIn(domain);
247 }
248
249 // Returns true if this ref only appear in one constraint.
250 bool VariableIsUnique(int ref) const;
251 bool VariableIsUniqueAndRemovable(int ref) const;
252
253 // Returns true if this ref no longer appears in the model.
254 bool VariableIsNotUsedAnymore(int ref) const;
255
256 // Functions to make sure that once we remove a variable, we no longer reuse
257 // it.
258 void MarkVariableAsRemoved(int ref);
259 bool VariableWasRemoved(int ref) const;
260
261 // Same as VariableIsUniqueAndRemovable() except that in this case the
262 // variable also appear in the objective in addition to a single constraint.
263 bool VariableWithCostIsUnique(int ref) const;
264 bool VariableWithCostIsUniqueAndRemovable(int ref) const;
265
266 // Returns true if an integer variable is only appearing in the rhs of
267 // constraints of the form lit => var in domain. When this is the case, then
268 // we can usually remove this variable and replace these constraints with
269 // the proper constraints on the enforcement literals.
271
272 // Similar to VariableIsOnlyUsedInEncodingAndMaybeInObjective() for the case
273 // where we have one extra constraint instead of the objective. Sometimes it
274 // is possible to transfer the linear1 domain restrictions to another
275 // variable. for instance if the other constraint is of the form Y = abs(X) or
276 // Y = X^2, then a domain restriction on Y can be transferred to X. We can
277 // then move the extra constraint to the mapping model and remove one
278 // variable. This happens on the flatzinc celar problems for instance.
280
281 // Returns false if the new domain is empty. Sets 'domain_modified' (if
282 // provided) to true iff the domain is modified otherwise does not change it.
283 ABSL_MUST_USE_RESULT bool IntersectDomainWith(
284 int ref, const Domain& domain, bool* domain_modified = nullptr);
285
286 // Returns false if the 'lit' doesn't have the desired value in the domain.
287 ABSL_MUST_USE_RESULT bool SetLiteralToFalse(int lit);
288 ABSL_MUST_USE_RESULT bool SetLiteralToTrue(int lit);
289
290 // Same as IntersectDomainWith() but take a linear expression as input.
291 // If this expression if of size > 1, this does nothing for now, so it will
292 // only propagates for constant and affine expression.
293 ABSL_MUST_USE_RESULT bool IntersectDomainWith(
294 const LinearExpressionProto& expr, const Domain& domain,
295 bool* domain_modified = nullptr);
296
297 // This function always return false. It is just a way to make a little bit
298 // more sure that we abort right away when infeasibility is detected.
299 ABSL_MUST_USE_RESULT bool NotifyThatModelIsUnsat(
300 absl::string_view message = "") {
301 // TODO(user): Report any explanation for the client in a nicer way?
302 SOLVER_LOG(logger_, "INFEASIBLE: '", message, "'");
303 is_unsat_ = true;
304 return false;
305 }
306 bool ModelIsUnsat() const { return is_unsat_; }
307
308 // Stores a description of a rule that was just applied to have a summary of
309 // what the presolve did at the end.
310 void UpdateRuleStats(const std::string& name, int num_times = 1);
311
312 // Updates the constraints <-> variables graph. This needs to be called each
313 // time a constraint is modified.
315
316 // At the beginning of the presolve, we delay the costly creation of this
317 // "graph" until we at least ran some basic presolve. This is because during
318 // a LNS neighborhood, many constraints will be reduced significantly by
319 // this "simple" presolve.
321
322 // Calls UpdateConstraintVariableUsage() on all newly created constraints.
324
325 // Returns true if our current constraints <-> variables graph is ok.
326 // This is meant to be used in DEBUG mode only.
328
329 // Loop over all variable and return true if one of them is only used in
330 // affine relation and is not a representative. This is in O(num_vars) and
331 // only meant to be used in DCHECKs.
332 bool HasUnusedAffineVariable() const;
333
334 // A "canonical domain" always have a MinOf() equal to zero.
335 // If needed we introduce a new variable with such canonical domain and
336 // add the relation X = Y + offset.
337 //
338 // This is useful in some corner case to avoid overflow.
339 //
340 // TODO(user): When we can always get rid of affine relation, it might be good
341 // to do a final pass to canonicalize all domains in a model after presolve.
342 void CanonicalizeVariable(int ref);
343
344 // Given the relation (X * coeff % mod = rhs % mod), this creates a new
345 // variable so that X = mod * Y + cte.
346 //
347 // This requires mod != 0 and coeff != 0.
348 //
349 // Note that the new variable will have a canonical domain (i.e. min == 0).
350 // We also do not create anything if this fixes the given variable or the
351 // relation simplifies. Returns false if the model is infeasible.
352 bool CanonicalizeAffineVariable(int ref, int64_t coeff, int64_t mod,
353 int64_t rhs);
354
355 // Adds the relation (var_x = coeff * var_y + offset) to the repository.
356 // Returns false if we detect infeasability because of this.
357 //
358 // Once the relation is added, it doesn't need to be enforced by a constraint
359 // in the model proto, since we will propagate such relation directly and add
360 // them to the proto at the end of the presolve.
361 //
362 // Note that this should always add a relation, even though it might need to
363 // create a new representative for both var_x and var_y in some cases. Like if
364 // x = 3z and y = 5t are already added, if we add x = 2y, we have 3z = 10t and
365 // can only resolve this by creating a new variable r such that z = 10r and t
366 // = 3r.
367 //
368 // All involved variables will be marked to appear in the special
369 // kAffineRelationConstraint. This will allow to identify when a variable is
370 // no longer needed (only appear there and is not a representative).
371 bool StoreAffineRelation(int var_x, int var_y, int64_t coeff, int64_t offset,
372 bool debug_no_recursion = false);
373
374 // Adds the fact that ref_a == ref_b using StoreAffineRelation() above.
375 // Returns false if this makes the problem infeasible.
376 bool StoreBooleanEqualityRelation(int ref_a, int ref_b);
377
378 // Returns the representative of a literal.
379 int GetLiteralRepresentative(int ref) const;
380
381 // Used for statistics.
382 int NumAffineRelations() const { return affine_relations_.NumRelations(); }
383
384 // Returns the representative of ref under the affine relations.
386
387 // To facilitate debugging.
388 std::string RefDebugString(int ref) const;
389 std::string AffineRelationDebugString(int ref) const;
390
391 // Makes sure the domain of ref and of its representative (ref = coeff * rep +
392 // offset) are in sync. Returns false on unsat.
393 bool PropagateAffineRelation(int var);
394 bool PropagateAffineRelation(int var, int rep, int64_t coeff, int64_t offset);
395
396 // Creates the internal structure for any new variables in working_model.
398
399 // This is a bit hacky. Clear some fields. See call site.
400 //
401 // TODO(user): The ModelCopier should probably not depend on the full context
402 // it only need to read/write domains and call UpdateRuleStats(), so we might
403 // want to split that part out so that we can just initialize the full context
404 // later. Alternatively, we could just move more complex part of the context
405 // out, like the graph, the encoding, the affine representative, and so on to
406 // individual and easier to manage classes.
407 void ResetAfterCopy();
408
409 // Clears the "rules" statistics.
410 void ClearStats();
411
412 // Inserts the given literal to encode var == value.
413 // If an encoding already exists, it adds the two implications between
414 // the previous encoding and the new encoding.
415 //
416 // Important: This does not update the constraint<->variable graph, so
417 // ConstraintVariableGraphIsUpToDate() will be false until
418 // UpdateNewConstraintsVariableUsage() is called.
419 //
420 // Returns false if the model become UNSAT.
421 //
422 // TODO(user): This function is not always correct if
423 // !context->DomainOf(var).contains(value), we could make it correct but it
424 // might be a bit expansive to do so. For now we just have a DCHECK().
425 bool InsertVarValueEncoding(int literal, int var, int64_t value);
426
427 // Gets the associated literal if it is already created. Otherwise
428 // create it, add the corresponding constraints and returns it.
429 //
430 // Important: This does not update the constraint<->variable graph, so
431 // ConstraintVariableGraphIsUpToDate() will be false until
432 // UpdateNewConstraintsVariableUsage() is called.
433 int GetOrCreateVarValueEncoding(int ref, int64_t value);
434
435 // Gets the associated literal if it is already created. Otherwise
436 // create it, add the corresponding constraints and returns it.
437 //
438 // Important: This does not update the constraint<->variable graph, so
439 // ConstraintVariableGraphIsUpToDate() will be false until
440 // UpdateNewConstraintsVariableUsage() is called.
441 int GetOrCreateAffineValueEncoding(const LinearExpressionProto& expr,
442 int64_t value);
443
444 // If not already done, adds a Boolean to represent any integer variables that
445 // take only two values. Make sure all the relevant affine and encoding
446 // relations are updated.
447 //
448 // Note that this might create a new Boolean variable.
449 void CanonicalizeDomainOfSizeTwo(int var);
450
451 // Returns true if a literal attached to ref == var exists.
452 // It assigns the corresponding to `literal` if non null.
453 bool HasVarValueEncoding(int ref, int64_t value, int* literal = nullptr);
454
455 // Returns true if we have literal <=> var = value for all values of var.
456 //
457 // TODO(user): If the domain was shrunk, we can have a false positive.
458 // Still it means that the number of values removed is greater than the number
459 // of values not encoded.
460 bool IsFullyEncoded(int ref) const;
461
462 // This methods only works for affine expressions (checked).
463 // It returns true iff the expression is constant or its one variable is full
464 // encoded.
465 bool IsFullyEncoded(const LinearExpressionProto& expr) const;
466
467 // Stores the fact that literal implies var == value.
468 // It returns true if that information is new.
469 bool StoreLiteralImpliesVarEqValue(int literal, int var, int64_t value);
470
471 // Stores the fact that literal implies var != value.
472 // It returns true if that information is new.
473 bool StoreLiteralImpliesVarNEqValue(int literal, int var, int64_t value);
474
475 // Objective handling functions. We load it at the beginning so that during
476 // presolve we can work on the more efficient hash_map representation.
477 //
478 // Note that ReadObjectiveFromProto() makes sure that var_to_constraints of
479 // all the variable that appear in the objective contains -1. This is later
480 // enforced by all the functions modifying the objective.
481 //
482 // Note(user): Because we process affine relation only on
483 // CanonicalizeObjective(), it is possible that when processing a
484 // canonicalized linear constraint, we don't detect that a variable in affine
485 // relation is in the objective. For now this is fine, because when this is
486 // the case, we also have an affine linear constraint, so we can't really do
487 // anything with that variable since it appear in at least two constraints.
489 bool AddToObjectiveOffset(int64_t delta);
490 ABSL_MUST_USE_RESULT bool CanonicalizeOneObjectiveVariable(int var);
491 ABSL_MUST_USE_RESULT bool CanonicalizeObjective(bool simplify_domain = true);
492 void WriteObjectiveToProto() const;
493
494 // When the objective is singleton, we can always restrict the domain of var
495 // so that the current objective domain is non-constraining. Returns false
496 // on UNSAT.
498
499 // Some function need the domain to be up to date in the proto.
500 // This make sures our in-memory domain are written back to the proto.
501 void WriteVariableDomainsToProto() const;
502
503 // Checks if the given exactly_one is included in the objective, and simplify
504 // the objective by adding a constant value to all the exactly one terms.
505 //
506 // Returns true if a simplification was done.
507 bool ExploitExactlyOneInObjective(absl::Span<const int> exactly_one);
508
509 // We can always add a multiple of sum X - 1 == 0 to the objective.
510 // However, depending on which multiple we choose, this might break our
511 // overflow preconditions on the objective. So we return false and do nothing
512 // if this happens.
513 bool ShiftCostInExactlyOne(absl::Span<const int> exactly_one, int64_t shift);
514
515 // Allows to manipulate the objective coefficients.
516 void RemoveVariableFromObjective(int ref);
517 void AddToObjective(int var, int64_t value);
518 void AddLiteralToObjective(int ref, int64_t value);
519
520 // Given a variable defined by the given inequality that also appear in the
521 // objective, remove it from the objective by transferring its cost to other
522 // variables in the equality.
523 //
524 // Returns false, if the substitution cannot be done. This is the case if the
525 // model become UNSAT or if doing it will result in an objective that do not
526 // satisfy our overflow preconditions. Note that this can only happen if the
527 // substituted variable is not implied free (i.e. if its domain is smaller
528 // than the implied domain from the equality).
529 ABSL_MUST_USE_RESULT bool SubstituteVariableInObjective(
530 int var_in_equality, int64_t coeff_in_equality,
531 const ConstraintProto& equality);
532
533 // Objective getters.
534 const Domain& ObjectiveDomain() const { return objective_domain_; }
535 const absl::flat_hash_map<int, int64_t>& ObjectiveMap() const {
536 return objective_map_;
537 }
538 int64_t ObjectiveCoeff(int var) const {
539 DCHECK_GE(var, 0);
540 const auto it = objective_map_.find(var);
541 return it == objective_map_.end() ? 0 : it->second;
542 }
543
544 // Returns false if the variables in the objective with a positive (resp.
545 // negative) coefficient can freely decrease (resp. increase) within their
546 // domain (if we ignore the other constraints). Otherwise, returns true.
548 return objective_domain_is_constraining_;
549 }
550
551 // If var is an unused variable in an affine relation and is not a
552 // representative, we can remove it from the model. Note that this requires
553 // the variable usage graph to be up to date.
555
556 // Advanced usage. This should be called when a variable can be removed from
557 // the problem, so we don't count it as part of an affine relation anymore.
560
561 // Variable <-> constraint graph.
562 // The vector list is sorted and contains unique elements.
563 //
564 // Important: To properly handle the objective, var_to_constraints[objective]
565 // contains kObjectiveConstraint (i.e. -1) so that if the objective appear in
566 // only one constraint, the constraint cannot be simplified.
567 absl::Span<const int> ConstraintToVars(int c) const {
569 return constraint_to_vars_[c];
570 }
571 const absl::flat_hash_set<int>& VarToConstraints(int var) const {
573 return var_to_constraints_[var];
574 }
575 int IntervalUsage(int c) const {
577 if (c >= interval_usage_.size()) return 0;
578 return interval_usage_[c];
579 }
580
581 // Checks if a constraint contains an enforcement literal set to false,
582 // or if it has been cleared.
583 bool ConstraintIsInactive(int ct_index) const;
584
585 // Checks if a constraint contains an enforcement literal not fixed, and
586 // no enforcement literals set to false.
587 bool ConstraintIsOptional(int ct_ref) const;
588
589 // Make sure we never delete an "assumption" literal by using a special
590 // constraint for that.
592 for (const int ref : working_model->assumptions()) {
593 var_to_constraints_[PositiveRef(ref)].insert(kAssumptionsConstraint);
594 }
595 }
596
597 // The "expansion" phase should be done once and allow to transform complex
598 // constraints into basic ones (see cp_model_expand.h). Some presolve rules
599 // need to know if the expansion was ran before beeing applied.
600 bool ModelIsExpanded() const { return model_is_expanded_; }
601 void NotifyThatModelIsExpanded() { model_is_expanded_ = true; }
602
603 // The following helper adds the following constraint:
604 // result <=> (time_i <= time_j && active_i is true && active_j is true)
605 // and returns the (cached) literal result.
606 //
607 // Note that this cache should just be used temporarily and then cleared
608 // with ClearPrecedenceCache() because there is no mechanism to update the
609 // cached literals when literal equivalence are detected.
610 int GetOrCreateReifiedPrecedenceLiteral(const LinearExpressionProto& time_i,
611 const LinearExpressionProto& time_j,
612 int active_i, int active_j);
613
614 std::tuple<int, int64_t, int, int64_t, int64_t, int, int>
615 GetReifiedPrecedenceKey(const LinearExpressionProto& time_i,
616 const LinearExpressionProto& time_j, int active_i,
617 int active_j);
618
619 // Clear the precedence cache.
621
622 // Logs stats to the logger.
623 void LogInfo();
624
625 // This should be called only once after InitializeNewDomains() to load
626 // the hint, in order to maintain it as best as possible during presolve.
627 // Hint values outside the domain of their variable are adjusted to the
628 // nearest value in this domain. Missing hint values are completed when
629 // possible (e.g. for the model proto's fixed variables).
630 void LoadSolutionHint();
631
632 SolutionCrush& solution_crush() { return solution_crush_; }
633 // This is slow O(problem_size) but can be used to debug presolve, either by
634 // pinpointing the transition from feasible to infeasible or the other way
635 // around if for some reason the presolve drop constraint that it shouldn't.
637
638 SolverLogger* logger() const { return logger_; }
639 const SatParameters& params() const { return params_; }
640 TimeLimit* time_limit() { return time_limit_; }
641 ModelRandomGenerator* random() { return random_; }
642
643 CpModelProto* working_model = nullptr;
644 CpModelProto* mapping_model = nullptr;
645
646 // Number of "rules" applied. This should be equal to the sum of all numbers
647 // in stats_by_rule_name. This is used to decide if we should do one more pass
648 // of the presolve or not. Note that depending on the presolve transformation,
649 // a rule can correspond to a tiny change or a big change. Because of that,
650 // this isn't a perfect proxy for the efficacy of the presolve.
652
653 // Temporary storage.
654 std::vector<int> tmp_literals;
655 std::vector<Domain> tmp_term_domains;
656 std::vector<Domain> tmp_left_domains;
657 absl::flat_hash_set<int> tmp_literal_set;
658
659 // Each time a domain is modified this is set to true.
661
662 // Each time the constraint <-> variable graph is updated, we update this.
663 // A variable is added here iff its usage decreased and is now one or two.
665
666 // Advanced presolve. See this class comment.
668
669 // Adds a new constraint to the mapping proto. The version with the base
670 // constraint will copy that constraint to the new constraint.
671 //
672 // If the flag --cp_model_debug_postsolve is set, we will use the caller
673 // file/line number to add debug info in the constraint name() field.
674 ConstraintProto* NewMappingConstraint(absl::string_view file, int line);
675 ConstraintProto* NewMappingConstraint(const ConstraintProto& base_ct,
676 absl::string_view file, int line);
677
678 private:
679 void MaybeResizeIntervalData();
680
681 void EraseFromVarToConstraint(int var, int c);
682
683 // Helper to add an affine relation x = c.y + o to the given repository.
684 bool AddRelation(int x, int y, int64_t c, int64_t o, AffineRelation* repo);
685
686 void AddVariableUsage(int c);
687 void UpdateLinear1Usage(const ConstraintProto& ct, int c);
688
689 // Makes sure we only insert encoding about the current representative.
690 //
691 // Returns false if ref cannot take the given value (it might not have been
692 // propagated yet).
693 bool CanonicalizeEncoding(int* ref, int64_t* value);
694
695 // Inserts an half reified var value encoding (literal => var ==/!= value).
696 // It returns true if the new state is different from the old state.
697 // Not that if imply_eq is false, the literal will be stored in its negated
698 // form.
699 //
700 // Thus, if you detect literal <=> var == value, then two calls must be made:
701 // InsertHalfVarValueEncoding(literal, var, value, true);
702 // InsertHalfVarValueEncoding(NegatedRef(literal), var, value, false);
703 bool InsertHalfVarValueEncoding(int literal, int var, int64_t value,
704 bool imply_eq);
705
706 // Insert fully reified var-value encoding.
707 // Returns false if this make the problem infeasible.
708 bool InsertVarValueEncodingInternal(int literal, int var, int64_t value,
709 bool add_constraints);
710
711 SolverLogger* logger_;
712 const SatParameters& params_;
713 TimeLimit* time_limit_;
714 ModelRandomGenerator* random_;
715
716 // Initially false, and set to true on the first inconsistency.
717 bool is_unsat_ = false;
718
719 // The current domain of each variables.
720 std::vector<Domain> domains_;
721
722 SolutionCrush solution_crush_;
723
724 // Internal representation of the objective. During presolve, we first load
725 // the objective in this format in order to have more efficient substitution
726 // on large problems (also because the objective is often dense). At the end
727 // we convert it back to its proto form.
728 mutable bool objective_proto_is_up_to_date_ = false;
729 absl::flat_hash_map<int, int64_t> objective_map_;
730 int64_t objective_overflow_detection_;
731 mutable std::vector<std::pair<int, int64_t>> tmp_entries_;
732 bool objective_domain_is_constraining_ = false;
733 Domain objective_domain_;
734 double objective_offset_;
735 double objective_scaling_factor_;
736 int64_t objective_integer_before_offset_;
737 int64_t objective_integer_after_offset_;
738 int64_t objective_integer_scaling_factor_;
739
740 // Constraints <-> Variables graph.
741 std::vector<std::vector<int>> constraint_to_vars_;
742 std::vector<absl::flat_hash_set<int>> var_to_constraints_;
743
744 // Number of constraints of the form [lit =>] var in domain.
745 std::vector<int> constraint_to_linear1_var_;
746 std::vector<int> var_to_num_linear1_;
747
748 // We maintain how many time each interval is used.
749 std::vector<std::vector<int>> constraint_to_intervals_;
750 std::vector<int> interval_usage_;
751
752 // Used by GetTrueLiteral()/GetFalseLiteral().
753 bool true_literal_is_defined_ = false;
754 int true_literal_;
755
756 // Contains variables with some encoded value: encoding_[i][v] points
757 // to the literal attached to the value v of the variable i.
758 absl::flat_hash_map<int, absl::flat_hash_map<int64_t, SavedLiteral>>
759 encoding_;
760
761 // Contains the currently collected half value encodings:
762 // (literal, var, value), i.e.: literal => var ==/!= value
763 // The state is accumulated (adding x => var == value then !x => var != value)
764 // will deduce that x equivalent to var == value.
765 absl::flat_hash_map<std::tuple<int, int>, int64_t> eq_half_encoding_;
766 absl::flat_hash_map<std::tuple<int, int>, int64_t> neq_half_encoding_;
767
768 // This regroups all the affine relations between variables. Note that the
769 // constraints used to detect such relations will be removed from the model at
770 // detection time. But we mark all the variables in affine relations as part
771 // of the kAffineRelationConstraint.
772 AffineRelation affine_relations_;
773
774 // Used by SetVariableAsRemoved() and VariableWasRemoved().
775 absl::flat_hash_set<int> removed_variables_;
776
777 // Cache for the reified precedence literals created during the expansion of
778 // the reservoir constraint. This cache is only valid during the expansion
779 // phase, and is cleared afterwards.
780 absl::flat_hash_map<std::tuple<int, int64_t, int, int64_t, int64_t, int, int>,
781 int>
782 reified_precedences_cache_;
783
784 // Just used to display statistics on the presolve rules that were used.
785 absl::flat_hash_map<std::string, int> stats_by_rule_name_;
786
787 // Used by CanonicalizeLinearExpressionInternal().
788 std::vector<std::pair<int, int64_t>> tmp_terms_;
789
790 bool model_is_expanded_ = false;
791};
792
793// Utility function to load the current problem into a in-memory representation
794// that will be used for probing. Returns false if UNSAT.
795bool LoadModelForProbing(PresolveContext* context, Model* local_model);
796
797bool LoadModelForPresolve(const CpModelProto& model_proto, SatParameters params,
798 PresolveContext* context, Model* local_model,
799 absl::string_view name_for_logging);
800
801void CreateValidModelWithSingleConstraint(const ConstraintProto& ct,
802 const PresolveContext* context,
803 std::vector<int>* variable_mapping,
804 CpModelProto* mini_model);
805} // namespace sat
806} // namespace operations_research
807
808#endif // OR_TOOLS_SAT_PRESOLVE_CONTEXT_H_
DomainDeductions deductions
Advanced presolve. See this class comment.
ABSL_MUST_USE_RESULT bool CanonicalizeObjective(bool simplify_domain=true)
bool ExpressionIsSingleVariable(const LinearExpressionProto &expr) const
Returns true iff the expr is of the form 1 * var + 0.
bool CanonicalizeAffineVariable(int ref, int64_t coeff, int64_t mod, int64_t rhs)
ABSL_MUST_USE_RESULT bool IntersectDomainWith(int ref, const Domain &domain, bool *domain_modified=nullptr)
bool ExpressionIsALiteral(const LinearExpressionProto &expr, int *literal=nullptr) const
Returns true iff the expr is a literal (x or not(x)).
bool StoreLiteralImpliesVarEqValue(int literal, int var, int64_t value)
void ClearStats()
Clears the "rules" statistics.
bool StoreAffineRelation(int var_x, int var_y, int64_t coeff, int64_t offset, bool debug_no_recursion=false)
SparseBitset< int > modified_domains
Each time a domain is modified this is set to true.
ABSL_MUST_USE_RESULT bool SetLiteralToTrue(int lit)
int NewIntVarWithDefinition(const Domain &domain, absl::Span< const std::pair< int, int64_t > > definition, bool append_constraint_to_mapping_model=false)
bool DomainIsEmpty(int ref) const
Helpers to query the current domain of a variable.
ABSL_MUST_USE_RESULT bool SubstituteVariableInObjective(int var_in_equality, int64_t coeff_in_equality, const ConstraintProto &equality)
bool IntervalIsConstant(int ct_ref) const
Helper to query the state of an interval.
int NewIntVar(const Domain &domain)
Helpers to adds new variables to the presolved model.
bool ShiftCostInExactlyOne(absl::Span< const int > exactly_one, int64_t shift)
ABSL_MUST_USE_RESULT bool SetLiteralToFalse(int lit)
Returns false if the 'lit' doesn't have the desired value in the domain.
absl::Span< const int > ConstraintToVars(int c) const
ABSL_MUST_USE_RESULT bool NotifyThatModelIsUnsat(absl::string_view message="")
int GetOrCreateVarValueEncoding(int ref, int64_t value)
ABSL_MUST_USE_RESULT bool CanonicalizeOneObjectiveVariable(int var)
std::string IntervalDebugString(int ct_ref) const
bool ExpressionIsAffineBoolean(const LinearExpressionProto &expr) const
bool VariableIsOnlyUsedInLinear1AndOneExtraConstraint(int var) const
bool StoreBooleanEqualityRelation(int ref_a, int ref_b)
void AddImplyInDomain(int b, int x, const Domain &domain)
b => (x ∈ domain).
bool VariableIsOnlyUsedInEncodingAndMaybeInObjective(int var) const
std::tuple< int, int64_t, int, int64_t, int64_t, int, int > GetReifiedPrecedenceKey(const LinearExpressionProto &time_i, const LinearExpressionProto &time_j, int active_i, int active_j)
void CappedUpdateMinMaxActivity(int var, int64_t coeff, int64_t *min_activity, int64_t *max_activity)
Utility function.
std::pair< int64_t, int64_t > ComputeMinMaxActivity(const ProtoWithVarsAndCoeffs &proto) const
int NewBoolVar(absl::string_view source)
void LogInfo()
Logs stats to the logger.
void ClearPrecedenceCache()
Clear the precedence cache.
int NewBoolVarWithClause(absl::Span< const int > clause)
bool HasVarValueEncoding(int ref, int64_t value, int *literal=nullptr)
void RemoveVariableFromObjective(int ref)
Allows to manipulate the objective coefficients.
std::optional< int64_t > FixedValueOrNullopt(const LinearExpressionProto &expr) const
This is faster than testing IsFixed() + FixedValue().
absl::Span< const Domain > AllDomains() const
const Domain & ObjectiveDomain() const
Objective getters.
Domain DomainSuperSetOf(const LinearExpressionProto &expr) const
Return a super-set of the domain of the linear expression.
void InitializeNewDomains()
Creates the internal structure for any new variables in working_model.
bool VariableIsNotUsedAnymore(int ref) const
Returns true if this ref no longer appears in the model.
bool VariableIsUnique(int ref) const
Returns true if this ref only appear in one constraint.
ConstraintProto * NewMappingConstraint(absl::string_view file, int line)
int GetLiteralRepresentative(int ref) const
Returns the representative of a literal.
int GetOrCreateAffineValueEncoding(const LinearExpressionProto &expr, int64_t value)
int LiteralForExpressionMax(const LinearExpressionProto &expr) const
std::vector< int > tmp_literals
Temporary storage.
bool CanonicalizeLinearExpression(absl::Span< const int > enforcements, LinearExpressionProto *expr)
bool DomainOfVarIsIncludedIn(int var, const Domain &domain)
This function takes a positive variable reference.
void UpdateRuleStats(const std::string &name, int num_times=1)
AffineRelation::Relation GetAffineRelation(int ref) const
Returns the representative of ref under the affine relations.
void AddToObjective(int var, int64_t value)
const absl::flat_hash_set< int > & VarToConstraints(int var) const
std::string RefDebugString(int ref) const
To facilitate debugging.
void AddLiteralToObjective(int ref, int64_t value)
bool InsertVarValueEncoding(int literal, int var, int64_t value)
void UpdateNewConstraintsVariableUsage()
Calls UpdateConstraintVariableUsage() on all newly created constraints.
int NumAffineRelations() const
Used for statistics.
PresolveContext(Model *model, CpModelProto *cp_model, CpModelProto *mapping)
const absl::flat_hash_map< int, int64_t > & ObjectiveMap() const
std::string AffineRelationDebugString(int ref) const
bool StoreLiteralImpliesVarNEqValue(int literal, int var, int64_t value)
bool ExploitExactlyOneInObjective(absl::Span< const int > exactly_one)
bool DomainContains(int ref, int64_t value) const
int NewBoolVarWithConjunction(absl::Span< const int > conjunction)
int GetOrCreateReifiedPrecedenceLiteral(const LinearExpressionProto &time_i, const LinearExpressionProto &time_j, int active_i, int active_j)
bool CanonicalizeLinearConstraint(ConstraintProto *ct)
int Get(PresolveContext *context) const
Definition file.cc:169
bool LoadModelForProbing(PresolveContext *context, Model *local_model)
bool ScaleFloatingPointObjective(const SatParameters &params, SolverLogger *logger, CpModelProto *proto)
constexpr int kAffineRelationConstraint
constexpr int kAssumptionsConstraint
constexpr int kObjectiveConstraint
We use some special constraint index in our variable <-> constraint graph.
void CreateValidModelWithSingleConstraint(const ConstraintProto &ct, const PresolveContext *context, std::vector< int > *variable_mapping, CpModelProto *mini_model)
bool LoadModelForPresolve(const CpModelProto &model_proto, SatParameters params, PresolveContext *context, Model *local_model, absl::string_view name_for_logging)
In SWIG mode, we don't want anything besides these top-level includes.
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
ABSL_DECLARE_FLAG(bool, cp_model_debug_postsolve)
#define SOLVER_LOG(logger,...)
Definition logging.h:109