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