Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
precedences.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 ORTOOLS_SAT_PRECEDENCES_H_
15#define ORTOOLS_SAT_PRECEDENCES_H_
16
17#include <algorithm>
18#include <cstdint>
19#include <tuple>
20#include <utility>
21#include <variant>
22#include <vector>
23
24#include "absl/container/btree_set.h"
25#include "absl/container/flat_hash_map.h"
26#include "absl/container/flat_hash_set.h"
27#include "absl/container/inlined_vector.h"
28#include "absl/log/check.h"
29#include "absl/strings/str_format.h"
30#include "absl/types/span.h"
34#include "ortools/sat/integer.h"
36#include "ortools/sat/model.h"
39#include "ortools/sat/util.h"
40#include "ortools/util/rev.h"
42
43namespace operations_research {
44namespace sat {
45
46// This file defines several classes to manage bounds of expressions of the form
47// `a*x + b*y <= upper_bound`. The `a*x + b*y` expressions are stored in a
48// `LinearExpression2` object, which is often canonicalized and GCD-reduced,
49// with the bound being divided by the GCD.
50//
51// To efficiently store and query such bounds in different contexts, we map each
52// `LinearExpression2` expressions for which we have a non-trivial bound
53// to a `LinearExpression2Index`, managed by the `Linear2Indices` class.
54//
55// Most callers of this class should use the `Linear2Bounds` class, which hides
56// the complexity of the different ways such bounds are deduced and allow:
57// - knowing the bound of a given expression at current level;
58// - getting the literals and integer literals that can be used to explain that
59// bound;
60// - pushing a new bound to an expression.
61//
62// Other classes in this file dealing with the current level bounds:
63// - `EnforcedLinear2Bounds`: Store the best relation of the form
64// `{lits} => a*x + b*y <= ub` that is non-trivial at the current level.
65// - `Linear2BoundsFromLinear3`: Class that keeps the best upper bound at the
66// current level for `a*x + b*y` from all the linear3 relations of the
67// form `a*x + b*y + c*z <= d`.
68//
69// Classes in this file dealing with root-level bounds or implications:
70// - `RootLevelLinear2Bounds`: Holds all the non-trivial bounds of the form
71// `a*x + b*y <= ub` at root level.
72// - `ConditionalLinear2Bounds`: Holds all the relations of the form
73// `{lits} => a*x + b*y <= ub` that are defined in the model.
74// - `ReifiedLinear2Bounds`: Store all the relations of the form
75// `{lits} <=> a*x + b*y <= ub` that are defined in the model. Also stores all
76// the relations of the form `a*x + b*y + c*z == d`.
77//
78// Other classes in this file:
79// - `Linear2Watcher`: Allow a propagator to be called back when a bound on a
80// given linear2 changed.
81// - `TransitivePrecedencesEvaluator`: Computes the transitive closure of a
82// DAG of `a*x + b*y <= expr` relations that are stored in
83// `RootLevelLinear2Bounds`.
84
85DEFINE_STRONG_INDEX_TYPE(LinearExpression2Index);
86const LinearExpression2Index kNoLinearExpression2Index(-1);
87inline LinearExpression2Index NegationOf(LinearExpression2Index i) {
88 return LinearExpression2Index(i.value() ^ 1);
89}
90
91inline bool Linear2IsPositive(LinearExpression2Index i) {
92 return (i.value() & 1) == 0;
93}
94
95inline LinearExpression2Index PositiveLinear2(LinearExpression2Index i) {
96 return LinearExpression2Index(i.value() & (~1));
97}
98
99// Class to hold a list of LinearExpression2 that have (potentially) non-trivial
100// bounds. This class is overzealous, in the sense that if a linear2 is in the
101// list, it does not necessarily mean that it has a non-trivial bound, but the
102// converse is true: if a linear2 is not in the list,
103// Linear2Bounds::GetUpperBound() will return a trivial bound.
105 public:
106 Linear2Indices() = default;
107
108 // Returns a never-changing index for the given linear expression.
109 // The expression must already be canonicalized and divided by its GCD.
110 LinearExpression2Index AddOrGet(LinearExpression2 expr);
111
112 // Returns a never-changing index for the given linear expression if it is
113 // potentially non-trivial, otherwise returns kNoLinearExpression2Index. The
114 // expression must already be canonicalized and divided by its GCD.
115 LinearExpression2Index GetIndex(LinearExpression2 expr) const;
116
117 LinearExpression2 GetExpression(LinearExpression2Index index) const;
118
119 // Return all positive linear2 expressions that have a potentially non-trivial
120 // bound. When calling this code it is often a good idea to check both the
121 // expression on the span and its negation. The order is fixed forever and
122 // this span can only grow by appending new expressions.
123 absl::Span<const LinearExpression2> GetStoredLinear2Indices() const {
124 return exprs_;
125 }
126
127 // Return a list of all potentially non-trivial LinearExpression2Indexes
128 // containing a given variable.
129 absl::Span<const LinearExpression2Index> GetAllLinear2ContainingVariable(
130 IntegerVariable var) const;
131
132 // Return a list of all potentially non-trivial LinearExpression2Indexes
133 // containing a given pair of variables.
134 absl::Span<const LinearExpression2Index> GetAllLinear2ContainingVariables(
135 IntegerVariable var1, IntegerVariable var2) const;
136
137 private:
138 std::vector<LinearExpression2> exprs_;
139 absl::flat_hash_map<LinearExpression2, int> expr_to_index_;
140
141 // Map to implement GetAllBoundsContainingVariable().
142 absl::flat_hash_map<IntegerVariable,
143 absl::InlinedVector<LinearExpression2Index, 2>>
144 var_to_bounds_;
145 // Map to implement GetAllBoundsContainingVariables().
146 absl::flat_hash_map<std::pair<IntegerVariable, IntegerVariable>,
147 absl::InlinedVector<LinearExpression2Index, 1>>
148 var_pair_to_bounds_;
149};
150
151// Simple "watcher" class that will be notified if a linear2 bound changed. It
152// can also be queried to see if LinearExpression2 involving a specific variable
153// changed since last time.
155 public:
156 explicit Linear2Watcher(Model* model)
157 : watcher_(model->GetOrCreate<GenericLiteralWatcher>()) {}
158
159 // This assumes `expr` is canonicalized and divided by its gcd.
161
162 // Register a GenericLiteralWatcher() id so that propagation is called as
163 // soon as a bound on a linear2 changed.
164 void WatchAllLinearExpressions2(int id) { propagator_ids_.insert(id); }
165
166 // Allow to know if some bounds changed since last query.
167 int64_t Timestamp() const { return timestamp_; }
168 int64_t VarTimestamp(IntegerVariable var);
169
170 private:
171 GenericLiteralWatcher* watcher_;
172
173 int64_t timestamp_ = 0;
175 absl::btree_set<int> propagator_ids_;
176};
177
178// This holds all the relation lhs <= linear2 <= rhs that are true at level
179// zero. It is the source of truth across all the solver for such bounds.
181 public:
183 : integer_trail_(model->GetOrCreate<IntegerTrail>()),
184 linear2_watcher_(model->GetOrCreate<Linear2Watcher>()),
185 shared_stats_(model->GetOrCreate<SharedStatistics>()),
186 lin2_indices_(model->GetOrCreate<Linear2Indices>()),
187 cp_model_mapping_(model->GetOrCreate<CpModelMapping>()),
188 shared_linear2_bounds_(model->Mutable<SharedLinear2Bounds>()),
189 shared_linear2_bounds_id_(
190 shared_linear2_bounds_ == nullptr
191 ? 0
192 : shared_linear2_bounds_->RegisterNewId(model->Name())) {}
193
195
196 // Add a relation lb <= expr <= ub. If expr is not a proper linear2 expression
197 // (e.g. 0*x + y, y + y, y - y) it will be ignored.
198 // Returns a pair saying whether the lower/upper bounds for this expr became
199 // more restricted than what was currently stored.
200 std::pair<bool, bool> Add(LinearExpression2 expr, IntegerValue lb,
201 IntegerValue ub) {
202 if (integer_trail_->LevelZeroUpperBound(expr) <= ub &&
203 integer_trail_->LevelZeroLowerBound(expr) >= lb) {
204 return {false, false};
205 }
206 const bool negated = expr.CanonicalizeAndUpdateBounds(lb, ub);
207 if (expr.coeffs[0] == 0 || expr.coeffs[1] == 0) return {false, false};
208 const LinearExpression2Index index = lin2_indices_->AddOrGet(expr);
209 bool ub_changed = AddUpperBound(index, ub);
210 bool lb_changed = AddUpperBound(NegationOf(index), -lb);
211 if (negated) {
212 std::swap(lb_changed, ub_changed);
213 }
214 return {lb_changed, ub_changed};
215 }
216
217 // Same as above, but only update the upper bound.
218 bool AddUpperBound(LinearExpression2 expr, IntegerValue ub) {
219 if (integer_trail_->LevelZeroUpperBound(expr) <= ub) return false;
221 if (expr.coeffs[0] == 0 || expr.coeffs[1] == 0) return false;
222 const IntegerValue gcd = expr.DivideByGcd();
223 ub = FloorRatio(ub, gcd);
224 return AddUpperBound(lin2_indices_->AddOrGet(expr), ub);
225 }
226
227 bool AddLowerBound(LinearExpression2 expr, IntegerValue lb) {
228 expr.Negate();
229 return AddUpperBound(expr, -lb);
230 }
231
232 bool AddLowerBound(LinearExpression2Index index, IntegerValue lb) {
233 return AddUpperBound(NegationOf(index), -lb);
234 }
235
236 // All modifications go through this function.
237 bool AddUpperBound(LinearExpression2Index index, IntegerValue ub);
238
239 IntegerValue LevelZeroUpperBound(LinearExpression2 expr) const {
241 if (expr.coeffs[0] == 0 || expr.coeffs[1] == 0) {
242 return integer_trail_->LevelZeroUpperBound(expr);
243 }
244 const IntegerValue gcd = expr.DivideByGcd();
245 const LinearExpression2Index index = lin2_indices_->GetIndex(expr);
246 if (index == kNoLinearExpression2Index) {
247 return integer_trail_->LevelZeroUpperBound(expr);
248 }
249 return CapProdI(gcd, LevelZeroUpperBound(index));
250 }
251
252 IntegerValue LevelZeroUpperBound(LinearExpression2Index index) const {
253 const LinearExpression2 expr = lin2_indices_->GetExpression(index);
254 // TODO(user): Remove the expression from the root_level_relations_ if
255 // the zero-level bound got more restrictive.
256 return std::min(integer_trail_->LevelZeroUpperBound(expr),
257 GetUpperBoundNoTrail(index));
258 }
259
260 // Return a list of (expr <= ub) sorted by expr. They are guaranteed to be
261 // better than the trivial upper bound.
262 std::vector<std::pair<LinearExpression2, IntegerValue>>
264
265 // Return a list of (lb <= expr <= ub), with expr.vars[0] = var, where at
266 // least one of the bounds is non-trivial and the potential other non-trivial
267 // bound is tight.
268 //
269 // As the class name indicates, all bounds are level zero ones.
270 std::vector<std::tuple<LinearExpression2, IntegerValue, IntegerValue>>
271 GetAllBoundsContainingVariable(IntegerVariable var) const;
272
273 // Return a list of (lb <= expr <= ub), with expr.vars = {var1, var2}, where
274 // at least one of the bounds is non-trivial and the potential other
275 // non-trivial bound is tight.
276 //
277 // As the class name indicates, all bounds are level zero ones.
278 std::vector<std::tuple<LinearExpression2, IntegerValue, IntegerValue>>
279 GetAllBoundsContainingVariables(IntegerVariable var1,
280 IntegerVariable var2) const;
281
282 // For a given variable `var`, return all variables `other` so that
283 // LinearExpression2(var, other, 1, 1) has a non trivial upper bound.
284 // Note that using negation one can also recover x + y >= lb and x - y <= ub.
285 absl::Span<const std::pair<IntegerVariable, LinearExpression2Index>>
286 GetVariablesInSimpleRelation(IntegerVariable var) const;
287
288 // For all pairs of relation 'a + var <= x' and 'neg(var) + b <= y' try to add
289 // 'a + b <= x + y' if that relation is better.
290 //
291 // This can be quadratic. Returns the amount of "work" done, and abort if
292 // we reach the limit. This uses GetVariablesInSimpleRelation().
293 int AugmentSimpleRelations(IntegerVariable var, int work_limit);
294
296 IntegerValue ub) const;
297
298 // Low-level function that returns the zero-level upper bound if it is
299 // non-trivial. Otherwise returns kMaxIntegerValue. This is a different
300 // behavior from LevelZeroUpperBound() that would return the implied
301 // zero-level bound from the trail for trivial ones. `expr` must be
302 // canonicalized and gcd-reduced.
303 IntegerValue GetUpperBoundNoTrail(LinearExpression2Index index) const;
304
305 int64_t num_updates() const { return num_updates_; }
306
307 private:
308 IntegerTrail* integer_trail_;
309 Linear2Watcher* linear2_watcher_;
310 SharedStatistics* shared_stats_;
311 Linear2Indices* lin2_indices_;
312 CpModelMapping* cp_model_mapping_;
313 SharedLinear2Bounds* shared_linear2_bounds_; // Might be nullptr.
314
315 const int shared_linear2_bounds_id_;
316
318 best_upper_bounds_;
319
320 // coeff_one_var_lookup_[var] contains all the other_var such that we have a
321 // linear2 relation var + other_var <= ub. We also store that relation index.
324 IntegerVariable,
325 std::vector<std::pair<IntegerVariable, LinearExpression2Index>>>
326 coeff_one_var_lookup_;
327
328 int64_t num_updates_ = 0;
329};
330
332 IntegerVariable var;
333 std::vector<int> indices;
334 std::vector<IntegerValue> offsets;
335};
336
337// This class is used to compute the transitive closure of the level-zero
338// precedence relations.
339//
340// TODO(user): Support non-DAG like graph.
342 public:
344 : params_(model->GetOrCreate<SatParameters>()),
345 integer_trail_(model->GetOrCreate<IntegerTrail>()),
346 shared_stats_(model->GetOrCreate<SharedStatistics>()),
347 root_level_bounds_(model->GetOrCreate<RootLevelLinear2Bounds>()) {
348 // Call Build() each time we go back to level zero.
349 model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
350 [this]() { return Build(); });
351 }
352
353 // Returns a set of relations var >= max_i(vars[index[i]] + offsets[i]).
354 //
355 // This currently only works if the precedence relation form a DAG.
356 // If not we will just abort. TODO(user): generalize.
357 //
358 // For more efficiency, this method ignores all linear2 expressions with any
359 // coefficient different from 1.
360 //
361 // TODO(user): Put some work limit in place, as this can be slow. Complexity
362 // is in O(vars.size()) * num_arcs.
363 //
364 // TODO(user): Since we don't need ALL precedences, we could just work on a
365 // sub-DAG of the full precedence graph instead of aborting. Or we can just
366 // support the general non-DAG cases.
367 //
368 // TODO(user): Many relations can be redundant. Filter them.
369 void ComputeFullPrecedences(absl::Span<const IntegerVariable> vars,
370 std::vector<FullIntegerPrecedence>* output);
371
372 // The current code requires the internal data to be processed once all
373 // root-level relations are loaded.
374 //
375 // If we don't have too many variable, we compute the full transitive closure
376 // and then push back to RootLevelLinear2Bounds if there is a relation between
377 // two variables. This can be used to optimize some scheduling propagation and
378 // reasons.
379 //
380 // Warning: If there are too many, this will NOT push all relations.
381 bool Build();
382
383 private:
384 SatParameters* params_;
385 IntegerTrail* integer_trail_;
386 SharedStatistics* shared_stats_;
387 RootLevelLinear2Bounds* root_level_bounds_;
388
389 int64_t build_timestamp_ = -1;
390 bool is_dag_ = false;
391 std::vector<IntegerVariable> topological_order_;
392};
393
394// Store the best non-trivial relation of the form "{lits} => a*x + b*y <= ub"
395// for which `{lits}` are assigned tp true at the current level.
397 public:
399 : params_(*model->GetOrCreate<SatParameters>()),
400 trail_(model->GetOrCreate<Trail>()),
401 integer_trail_(model->GetOrCreate<IntegerTrail>()),
402 linear2_watcher_(model->GetOrCreate<Linear2Watcher>()),
403 root_level_bounds_(model->GetOrCreate<RootLevelLinear2Bounds>()),
404 shared_stats_(model->GetOrCreate<SharedStatistics>()),
405 lin2_indices_(model->GetOrCreate<Linear2Indices>()) {
406 integer_trail_->RegisterReversibleClass(this);
407 }
408
409 ~EnforcedLinear2Bounds() override;
410
411 // Adds add relation (enf => expr <= rhs) that is assumed to be true at
412 // the current level.
413 //
414 // It will be automatically reverted via the SetLevel() functions that is
415 // called before any integer propagations trigger.
416 //
417 // This is assumed to be called when a relation becomes true (enforcement are
418 // assigned) and when it becomes false in reverse order (CHECKed).
419 //
420 // If expr is not a proper linear2 expression (e.g. 0*x + y, y + y, y - y) it
421 // will be ignored.
422 void PushConditionalRelation(absl::Span<const Literal> enforcements,
423 LinearExpression2Index index, IntegerValue rhs);
424
425 void PushConditionalRelation(absl::Span<const Literal> enforcements,
426 LinearExpression2 expr, IntegerValue rhs) {
428 if (expr.coeffs[0] == 0 || expr.coeffs[1] == 0) return;
429 const IntegerValue gcd = expr.DivideByGcd();
430 rhs = FloorRatio(rhs, gcd);
431 return PushConditionalRelation(enforcements, lin2_indices_->AddOrGet(expr),
432 rhs);
433 }
434
435 // Called each time we change decision level.
436 void SetLevel(int level) final;
437
439 if (trail_->CurrentDecisionLevel() != stored_level_) {
440 SetLevel(trail_->CurrentDecisionLevel());
441 }
442 }
443
444 // Returns a set of precedences such that we have a relation
445 // of the form vars[index] <= var + offset.
446 //
447 // All entries for the same variable will be contiguous and sorted by index.
448 // We only list variable with at least two entries. The up to date offset can
449 // be retrieved later via Linear2Bounds::UpperBound(lin2_index).
450 //
451 // This method currently ignores all linear2 expressions with any coefficient
452 // different from 1.
453 //
454 // TODO(user): Ideally this should be moved to a new class and maybe augmented
455 // with other kind of precedences.
457 IntegerVariable var;
459 LinearExpression2Index lin2_index;
460 };
461 void CollectPrecedences(absl::Span<const IntegerVariable> vars,
462 std::vector<PrecedenceData>* output);
463
464 // Low-level function that returns the upper bound if there is some enforced
465 // relations only. Otherwise always returns kMaxIntegerValue.
466 // `expr` must be canonicalized and gcd-reduced.
467 IntegerValue GetUpperBoundFromEnforced(LinearExpression2Index index) const;
468
470 LinearExpression2Index index, IntegerValue ub,
471 std::vector<Literal>* literal_reason,
472 std::vector<IntegerLiteral>* integer_reason) const;
473
474 private:
475 void CreateLevelEntryIfNeeded();
476
477 const SatParameters& params_;
478 Trail* trail_;
479 IntegerTrail* integer_trail_;
480 Linear2Watcher* linear2_watcher_;
481 RootLevelLinear2Bounds* root_level_bounds_;
482 SharedStatistics* shared_stats_;
483 Linear2Indices* lin2_indices_;
484
485 int64_t num_conditional_relation_updates_ = 0;
486 int stored_level_ = 0;
487
488 // Conditional stack for push/pop of conditional relations.
489 //
490 // TODO(user): this kind of reversible hash_map is already implemented in
491 // other part of the code. Consolidate.
492 struct ConditionalEntry {
493 ConditionalEntry(int p, IntegerValue r, LinearExpression2Index k,
494 absl::Span<const Literal> e)
495 : prev_entry(p), rhs(r), key(k), enforcements(e.begin(), e.end()) {}
496
497 int prev_entry;
498 IntegerValue rhs;
499 LinearExpression2Index key;
500 absl::InlinedVector<Literal, 4> enforcements;
501 };
502 std::vector<ConditionalEntry> conditional_stack_;
503 std::vector<std::pair<int, int>> level_to_stack_size_;
504
505 // This is always stored in the form (expr <= rhs).
506 // The conditional relations contains indices in the conditional_stack_.
508
509 // Store for each variable x, the variables y that appears alongside it in
510 // lit => x + y <= ub. Note that conditional_var_lookup_ is updated on
511 // dive/backtrack.
513 IntegerVariable,
514 std::vector<std::pair<IntegerVariable, LinearExpression2Index>>>
515 conditional_var_lookup_;
516
517 // Temp data for CollectPrecedences.
518 std::vector<IntegerVariable> var_with_positive_degree_;
521 std::vector<PrecedenceData> tmp_precedences_;
522};
523
524// A relation of the form enforcement => expr \in [lhs, rhs].
525// Note that the [lhs, rhs] interval should always be within [min_activity,
526// max_activity] where the activity is the value of expr.
527struct Relation {
530 IntegerValue lhs;
531 IntegerValue rhs;
532
533 bool operator==(const Relation& other) const {
534 return enforcement == other.enforcement && expr == other.expr &&
535 lhs == other.lhs && rhs == other.rhs;
536 }
537
538 template <typename Sink>
539 friend void AbslStringify(Sink& sink, const Relation& relation) {
540 absl::Format(&sink, "%s => %v in [%v, %v]",
541 relation.enforcement.DebugString(), relation.expr,
542 relation.lhs, relation.rhs);
543 }
544};
545
546class ReifiedLinear2Bounds;
547
548// A repository of all root-level relations of the type l => (a*x + b*y <= ub).
549//
550// TODO(user): This is not always needed, find a way to clean this once we
551// don't need it.
553 public:
555 : reified_linear2_bounds_(model->GetOrCreate<ReifiedLinear2Bounds>()),
556 shared_stats_(model->GetOrCreate<SharedStatistics>()) {}
558
559 int size() const { return relations_.size(); }
560
561 // The linear2 expression in the returned relation is guaranteed to be
562 // normalized (ie., SimpleCanonicalization() has been called on it and it's
563 // GCD-reduced).
564 const Relation& relation(int index) const { return relations_[index]; }
565
566 // Returns the indices of the relations that are enforced by the given
567 // literal.
568 absl::Span<const int> IndicesOfRelationsEnforcedBy(LiteralIndex lit) const {
569 if (lit >= lit_to_relations_.size()) return {};
570 return lit_to_relations_[lit];
571 }
572
573 // Adds a conditional relation lit => expr \in [lhs, rhs] (one of the coeffs
574 // can be zero).
575 void Add(Literal lit, LinearExpression2 expr, IntegerValue lhs,
576 IntegerValue rhs);
577
578 // Adds a partial conditional relation between two variables, with unspecified
579 // coefficients and bounds.
580 void AddPartialRelation(Literal lit, IntegerVariable a, IntegerVariable b);
581
582 // Builds the literal to relations mapping. This should be called once all the
583 // relations have been added.
584 void Build();
585
586 private:
587 ReifiedLinear2Bounds* reified_linear2_bounds_;
588 SharedStatistics* shared_stats_;
589 bool is_built_ = false;
590 int num_enforced_relations_ = 0;
591 int num_encoded_equivalences_ = 0;
592 std::vector<Relation> relations_;
594};
595
596// Class that keeps the best upper bound for a*x + b*y by using all the linear3
597// relations of the form a*x + b*y + c*z <= d.
599 public:
600 explicit Linear2BoundsFromLinear3(Model* model);
602
603 // If the given upper bound evaluate better than the current one we have, this
604 // will replace it and returns true, otherwise it returns false.
605 bool AddAffineUpperBound(LinearExpression2Index lin2_index,
606 IntegerValue lin_expr_gcd,
607 AffineExpression affine_ub);
608
611 if (expr.coeffs[0] == 0 || expr.coeffs[1] == 0) return false;
612 const IntegerValue gcd = expr.DivideByGcd();
613 return AddAffineUpperBound(lin2_indices_->AddOrGet(expr), gcd, affine_ub);
614 }
615
616 // Most users should just use Linear2Bounds::UpperBound() instead.
617 //
618 // Returns the upper bound only if there is some relations coming from a
619 // linear3. Otherwise always returns kMaxIntegerValue.
620 // `expr` must be canonicalized and gcd-reduced.
621 IntegerValue GetUpperBoundFromLinear3(
622 LinearExpression2Index lin2_index) const;
623
624 // Most users should use Linear2Bounds::AddReasonForUpperBoundLowerThan()
625 // instead.
626 //
627 // Adds the reason for GetUpperBoundFromLinear3() to be <= ub.
628 // `expr` must be canonicalized and gcd-reduced.
630 LinearExpression2Index lin2_index, IntegerValue ub,
631 std::vector<Literal>* literal_reason,
632 std::vector<IntegerLiteral>* integer_reason) const;
633
634 private:
635 IntegerTrail* integer_trail_;
636 Trail* trail_;
637 Linear2Watcher* linear2_watcher_;
638 GenericLiteralWatcher* watcher_;
639 SharedStatistics* shared_stats_;
640 RootLevelLinear2Bounds* root_level_bounds_;
641 Linear2Indices* lin2_indices_;
642
643 int64_t num_affine_updates_ = 0;
644
645 // This stores linear2 <= AffineExpression / divisor.
646 //
647 // Note(user): This is a "cheap way" to not have to deal with backtracking, If
648 // we have many possible AffineExpression that bounds a LinearExpression2, we
649 // keep the best one during "search dive" but on backtrack we might have a
650 // sub-optimal relation.
651 util_intops::StrongVector<LinearExpression2Index,
652 std::pair<AffineExpression, IntegerValue>>
653 best_affine_ub_;
654};
655
656// TODO(user): Merge with ConditionalLinear2Bounds. Note that this one provides
657// different indexing though, so it could be kept separate.
659 public:
660 explicit ReifiedLinear2Bounds(Model* model);
662
663 // Register the fact that l <=> ( expr <= ub ).
664 // `expr` must already be canonicalized and gcd-reduced.
665 // These are considered equivalence relation.
667 IntegerValue ub);
668
669 // Add a linear3 of the form vars[i]*coeffs[i] = activity that is not
670 // enforced and valid at level zero.
671 void AddLinear3(absl::Span<const IntegerVariable> vars,
672 absl::Span<const int64_t> coeffs, int64_t activity);
673
674 // Returns ReifiedBoundType if we don't have a literal <=> ( expr <= ub ), or
675 // returns that literal if we have one. `expr` must be canonicalized and
676 // gcd-reduced.
682 std::variant<ReifiedBoundType, Literal, IntegerLiteral> GetEncodedBound(
683 LinearExpression2 expr, IntegerValue ub);
684
685 std::pair<AffineExpression, IntegerValue> GetLinear3Bound(
686 LinearExpression2Index lin2_index) const;
687
688 private:
689 RootLevelLinear2Bounds* best_root_level_bounds_;
690 Linear2Indices* lin2_indices_;
691 SharedStatistics* shared_stats_;
692
693 // This stores divisor * linear2 = AffineExpression similarly to
694 // Linear2BoundsFromLinear3. The difference here is that we only store linear3
695 // that are equality, but irrespective of whether it constraint any linear2 at
696 // the current level. If there is more than one expression for a given
697 // linear2, we will keep the one with the smallest divisor.
698 util_intops::StrongVector<LinearExpression2Index,
699 std::pair<AffineExpression, IntegerValue>>
700 linear3_bounds_;
701
702 // This stores relations l <=> (linear2 <= rhs).
703 absl::flat_hash_map<std::pair<LinearExpression2Index, IntegerValue>, Literal>
704 relation_to_lit_;
705
706 // This is used to detect relations that become fixed at level zero and
707 // "upgrade" them to non-enforced relations. Because we only do that when
708 // we fix variable, a linear scan shouldn't be too bad and is relatively
709 // compact memory wise.
710 absl::flat_hash_set<BooleanVariable> variable_appearing_in_reified_relations_;
711 std::vector<std::tuple<Literal, LinearExpression2Index, IntegerValue>>
712 all_reified_relations_;
713
714 int64_t num_linear3_relations_ = 0;
715 int64_t num_relations_fixed_at_root_level_ = 0;
716};
717
718// Simple wrapper around the different repositories for bounds of linear2.
719// This should provide the best bounds.
721 public:
722 explicit Linear2Bounds(Model* model)
723 : integer_trail_(model->GetOrCreate<IntegerTrail>()),
724 root_level_bounds_(model->GetOrCreate<RootLevelLinear2Bounds>()),
725 enforced_bounds_(model->GetOrCreate<EnforcedLinear2Bounds>()),
726 linear3_bounds_(model->GetOrCreate<Linear2BoundsFromLinear3>()),
727 lin2_indices_(model->GetOrCreate<Linear2Indices>()),
728 reified_lin2_bounds_(model->GetOrCreate<ReifiedLinear2Bounds>()),
729 trail_(model->GetOrCreate<Trail>()),
730 shared_stats_(model->GetOrCreate<SharedStatistics>()) {}
731
733
734 // Returns the best known upper-bound of the given LinearExpression2 at the
735 // current decision level. If its explanation is needed, it can be queried
736 // with the second function.
737 IntegerValue UpperBound(LinearExpression2 expr) const;
738 IntegerValue UpperBound(LinearExpression2Index lin2_index) const;
739
741 LinearExpression2 expr, IntegerValue ub,
742 std::vector<Literal>* literal_reason,
743 std::vector<IntegerLiteral>* integer_reason) const;
744
745 RelationStatus GetStatus(LinearExpression2 expr, IntegerValue lb,
746 IntegerValue ub) const;
747
748 // Like UpperBound() but do not consider the bounds coming from
749 // the individual variable bounds. This is faster.
750 IntegerValue NonTrivialUpperBound(LinearExpression2Index lin2_index) const;
751
752 // Given the new linear2 bounds and its reason, inspect our various repository
753 // to find the strongest way to push this new upper bound.
754 bool EnqueueLowerOrEqual(LinearExpression2 expr, IntegerValue ub,
755 absl::Span<const Literal> literal_reason,
756 absl::Span<const IntegerLiteral> integer_reason);
757
758 // For LazyReasonInterface.
759 std::string LazyReasonName() const final { return "Linear2Bounds"; }
760 void Explain(int id, IntegerLiteral to_explain, IntegerReason* reason) final;
761
762 private:
763 IntegerTrail* integer_trail_;
764 RootLevelLinear2Bounds* root_level_bounds_;
765 EnforcedLinear2Bounds* enforced_bounds_;
766 Linear2BoundsFromLinear3* linear3_bounds_;
767 Linear2Indices* lin2_indices_;
768 ReifiedLinear2Bounds* reified_lin2_bounds_;
769 Trail* trail_;
770 SharedStatistics* shared_stats_;
771
772 // This is used for the lazy-reason implemented in Explain().
774
775 int64_t enqueue_trivial_ = 0;
776 int64_t enqueue_degenerate_ = 0;
777 int64_t enqueue_true_at_root_level_ = 0;
778 int64_t enqueue_conflict_false_at_root_level_ = 0;
779 int64_t enqueue_individual_var_bounds_ = 0;
780 int64_t enqueue_literal_encoding_ = 0;
781 int64_t enqueue_integer_linear3_encoding_ = 0;
782};
783
784// Assuming level-zero bounds + any (var >= value) in the input map,
785// fills "output" with a "propagated" set of bounds assuming lit is true (by
786// using the relations enforced by lit, as well as the non-enforced ones).
787// Note that we will only fill bounds > level-zero ones in output.
788//
789// Returns false if the new bounds are infeasible at level zero.
790//
791// Important: by default this does not call output->clear() so we can take
792// the max with already inferred bounds.
794 const IntegerTrail& integer_trail,
795 const RootLevelLinear2Bounds& root_level_bounds,
796 const ConditionalLinear2Bounds& repository,
797 const ImpliedBounds& implied_bounds, Literal lit,
798 const absl::flat_hash_map<IntegerVariable, IntegerValue>& input,
799 absl::flat_hash_map<IntegerVariable, IntegerValue>* output);
800
801// Detects if at least one of a subset of linear of size 2 or 1, touching the
802// same variable, must be true. When this is the case we add a new propagator to
803// propagate that fact.
804//
805// TODO(user): Shall we do that on the main thread before the workers are
806// spawned? note that the probing version need the model to be loaded though.
808 public:
810 : repository_(*model->GetOrCreate<ConditionalLinear2Bounds>()),
811 implied_bounds_(*model->GetOrCreate<ImpliedBounds>()),
812 integer_trail_(*model->GetOrCreate<IntegerTrail>()),
813 shared_stats_(model->GetOrCreate<SharedStatistics>()) {}
814
816
817 // Advanced usage. To be called once all the constraints have been added to
818 // the model. This will detect GreaterThanAtLeastOneOfConstraint().
819 // Returns the number of added constraint.
820 //
821 // TODO(user): This can be quite slow, add some kind of deterministic limit
822 // so that we can use it all the time.
824 bool auto_detect_clauses = false);
825
826 private:
827 struct VariableConditionalAffineBound {
828 Literal enforcement_literal;
829 IntegerVariable var;
830 AffineExpression bound;
831 };
832
833 void AddRelationIfNonTrivial(
834 const Relation& r,
835 std::vector<VariableConditionalAffineBound>* clause_bounds) const;
836
837 // Given an existing clause, sees if it can be used to add "greater than at
838 // least one of" type of constraints. Returns the number of such constraint
839 // added.
840 int AddGreaterThanAtLeastOneOfConstraintsFromClause(
841 absl::Span<const Literal> clause, Model* model,
842 const CompactVectorVector<LiteralIndex, IntegerLiteral>&
843 implied_bounds_by_literal);
844
845 // Another approach for AddGreaterThanAtLeastOneOfConstraints(), this one
846 // might be a bit slow as it relies on the propagation engine to detect
847 // clauses between incoming arcs presence literals.
848 // Returns the number of added constraints.
849 int AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(
850 Model* model);
851
852 // Once we identified a clause and relevant indices, this build the
853 // constraint. Returns true if we actually add it.
854 bool AddRelationFromBounds(
855 IntegerVariable var, absl::Span<const Literal> clause,
856 absl::Span<const VariableConditionalAffineBound> bounds, Model* model);
857
858 ConditionalLinear2Bounds& repository_;
859 const ImpliedBounds& implied_bounds_;
860 IntegerTrail& integer_trail_;
861 SharedStatistics* shared_stats_;
862 int64_t num_detected_constraints_ = 0;
863 int64_t total_num_expressions_ = 0;
864 int64_t maximum_num_expressions_ = 0;
865};
866
867// This can be in a hot-loop, so we want to inline it if possible.
869 LinearExpression2Index lin2_index) const {
870 CHECK_NE(lin2_index, kNoLinearExpression2Index);
871 IntegerValue ub = kMaxIntegerValue;
872 ub = std::min(ub, root_level_bounds_->GetUpperBoundNoTrail(lin2_index));
873 ub = std::min(ub, enforced_bounds_->GetUpperBoundFromEnforced(lin2_index));
874 ub = std::min(ub, linear3_bounds_->GetUpperBoundFromLinear3(lin2_index));
875 return ub;
876}
877inline LinearExpression2Index Linear2Indices::GetIndex(
878 LinearExpression2 expr) const {
879 if (expr.coeffs[0] == 0 || expr.coeffs[1] == 0) {
881 }
882 DCHECK(expr.IsCanonicalized());
883 DCHECK_EQ(expr.DivideByGcd(), 1);
884 const bool negated = expr.NegateForCanonicalization();
885 auto it = expr_to_index_.find(expr);
886 if (it == expr_to_index_.end()) return kNoLinearExpression2Index;
887
888 const LinearExpression2Index positive_index(2 * it->second);
889 if (negated) {
890 return NegationOf(positive_index);
891 } else {
892 return positive_index;
893 }
894}
895
897 LinearExpression2Index index) const {
898 DCHECK_NE(index, kNoLinearExpression2Index);
899 const int lookup_index = index.value() / 2;
900 DCHECK_LT(lookup_index, exprs_.size());
901 if (Linear2IsPositive(index)) {
902 return exprs_[lookup_index];
903 } else {
904 LinearExpression2 result = exprs_[lookup_index];
905 result.Negate();
906 return result;
907 }
908}
909
910inline absl::Span<const LinearExpression2Index>
912 const IntegerVariable positive_var = PositiveVariable(var);
913 auto it = var_to_bounds_.find(positive_var);
914 if (it == var_to_bounds_.end()) return {};
915 return it->second;
916}
917
918inline absl::Span<const LinearExpression2Index>
920 IntegerVariable var2) const {
921 IntegerVariable positive_var1 = PositiveVariable(var1);
922 IntegerVariable positive_var2 = PositiveVariable(var2);
923 if (positive_var1 > positive_var2) {
924 std::swap(positive_var1, positive_var2);
925 }
926 auto it = var_pair_to_bounds_.find({positive_var1, positive_var2});
927 if (it == var_pair_to_bounds_.end()) return {};
928 return it->second;
929}
930
931} // namespace sat
932} // namespace operations_research
933
934#endif // ORTOOLS_SAT_PRECEDENCES_H_
Definition model.h:345
void AddPartialRelation(Literal lit, IntegerVariable a, IntegerVariable b)
absl::Span< const int > IndicesOfRelationsEnforcedBy(LiteralIndex lit) const
void Add(Literal lit, LinearExpression2 expr, IntegerValue lhs, IntegerValue rhs)
const Relation & relation(int index) const
void PushConditionalRelation(absl::Span< const Literal > enforcements, LinearExpression2Index index, IntegerValue rhs)
IntegerValue GetUpperBoundFromEnforced(LinearExpression2Index index) const
void AddReasonForUpperBoundLowerThan(LinearExpression2Index index, IntegerValue ub, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason) const
void PushConditionalRelation(absl::Span< const Literal > enforcements, LinearExpression2 expr, IntegerValue rhs)
void CollectPrecedences(absl::Span< const IntegerVariable > vars, std::vector< PrecedenceData > *output)
int AddGreaterThanAtLeastOneOfConstraints(Model *model, bool auto_detect_clauses=false)
IntegerValue GetUpperBoundFromLinear3(LinearExpression2Index lin2_index) const
bool AddAffineUpperBound(LinearExpression2 expr, AffineExpression affine_ub)
bool AddAffineUpperBound(LinearExpression2Index lin2_index, IntegerValue lin_expr_gcd, AffineExpression affine_ub)
void AddReasonForUpperBoundLowerThan(LinearExpression2Index lin2_index, IntegerValue ub, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason) const
void Explain(int id, IntegerLiteral to_explain, IntegerReason *reason) final
RelationStatus GetStatus(LinearExpression2 expr, IntegerValue lb, IntegerValue ub) const
std::string LazyReasonName() const final
bool EnqueueLowerOrEqual(LinearExpression2 expr, IntegerValue ub, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
void AddReasonForUpperBoundLowerThan(LinearExpression2 expr, IntegerValue ub, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason) const
IntegerValue NonTrivialUpperBound(LinearExpression2Index lin2_index) const
IntegerValue UpperBound(LinearExpression2 expr) const
absl::Span< const LinearExpression2Index > GetAllLinear2ContainingVariables(IntegerVariable var1, IntegerVariable var2) const
LinearExpression2Index GetIndex(LinearExpression2 expr) const
LinearExpression2Index AddOrGet(LinearExpression2 expr)
absl::Span< const LinearExpression2Index > GetAllLinear2ContainingVariable(IntegerVariable var) const
LinearExpression2 GetExpression(LinearExpression2Index index) const
absl::Span< const LinearExpression2 > GetStoredLinear2Indices() const
int64_t VarTimestamp(IntegerVariable var)
void NotifyBoundChanged(LinearExpression2 expr)
std::string DebugString() const
Definition sat_base.h:101
std::pair< AffineExpression, IntegerValue > GetLinear3Bound(LinearExpression2Index lin2_index) const
void AddBoundEncodingIfNonTrivial(Literal l, LinearExpression2 expr, IntegerValue ub)
std::variant< ReifiedBoundType, Literal, IntegerLiteral > GetEncodedBound(LinearExpression2 expr, IntegerValue ub)
void AddLinear3(absl::Span< const IntegerVariable > vars, absl::Span< const int64_t > coeffs, int64_t activity)
RelationStatus GetLevelZeroStatus(LinearExpression2 expr, IntegerValue lb, IntegerValue ub) const
bool AddLowerBound(LinearExpression2Index index, IntegerValue lb)
int AugmentSimpleRelations(IntegerVariable var, int work_limit)
absl::Span< const std::pair< IntegerVariable, LinearExpression2Index > > GetVariablesInSimpleRelation(IntegerVariable var) const
std::pair< bool, bool > Add(LinearExpression2 expr, IntegerValue lb, IntegerValue ub)
bool AddLowerBound(LinearExpression2 expr, IntegerValue lb)
std::vector< std::tuple< LinearExpression2, IntegerValue, IntegerValue > > GetAllBoundsContainingVariable(IntegerVariable var) const
IntegerValue LevelZeroUpperBound(LinearExpression2Index index) const
bool AddUpperBound(LinearExpression2 expr, IntegerValue ub)
IntegerValue GetUpperBoundNoTrail(LinearExpression2Index index) const
std::vector< std::pair< LinearExpression2, IntegerValue > > GetSortedNonTrivialUpperBounds() const
std::vector< std::tuple< LinearExpression2, IntegerValue, IntegerValue > > GetAllBoundsContainingVariables(IntegerVariable var1, IntegerVariable var2) const
IntegerValue LevelZeroUpperBound(LinearExpression2 expr) const
void ComputeFullPrecedences(absl::Span< const IntegerVariable > vars, std::vector< FullIntegerPrecedence > *output)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
bool PropagateLocalBounds(const IntegerTrail &integer_trail, const RootLevelLinear2Bounds &root_level_bounds, const ConditionalLinear2Bounds &repository, const ImpliedBounds &implied_bounds, Literal lit, const absl::flat_hash_map< IntegerVariable, IntegerValue > &input, absl::flat_hash_map< IntegerVariable, IntegerValue > *output)
LinearExpression2Index PositiveLinear2(LinearExpression2Index i)
Definition precedences.h:95
const LinearExpression2Index kNoLinearExpression2Index(-1)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
Definition integer.cc:52
IntegerVariable PositiveVariable(IntegerVariable i)
bool Linear2IsPositive(LinearExpression2Index i)
Definition precedences.h:91
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
OR-Tools root namespace.
ClosedInterval::Iterator end(ClosedInterval interval)
ClosedInterval::Iterator begin(ClosedInterval interval)
static int input(yyscan_t yyscanner)
#define DEFINE_STRONG_INDEX_TYPE(index_type_name)
std::vector< std::function< bool()> > callbacks
bool CanonicalizeAndUpdateBounds(IntegerValue &lb, IntegerValue &ub)
friend void AbslStringify(Sink &sink, const Relation &relation)
bool operator==(const Relation &other) const