Google OR-Tools v9.14
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 OR_TOOLS_SAT_PRECEDENCES_H_
15#define OR_TOOLS_SAT_PRECEDENCES_H_
16
17#include <algorithm>
18#include <cstdint>
19#include <deque>
20#include <functional>
21#include <utility>
22#include <vector>
23
24#include "absl/container/flat_hash_map.h"
25#include "absl/container/inlined_vector.h"
26#include "absl/log/check.h"
27#include "absl/types/span.h"
29#include "ortools/graph/graph.h"
30#include "ortools/sat/integer.h"
32#include "ortools/sat/model.h"
36#include "ortools/sat/util.h"
37#include "ortools/util/bitset.h"
38#include "ortools/util/rev.h"
40
41namespace operations_research {
42namespace sat {
43
45 IntegerVariable var;
46 std::vector<int> indices;
47 std::vector<IntegerValue> offsets;
48};
49
50// Stores all the precedences relation of the form "a*x + b*y <= ub"
51// that we could extract from the linear constraint of the model. These are
52// stored in a directed graph.
53//
54// TODO(user): Support conditional relation.
55// TODO(user): Support non-DAG like graph.
56// TODO(user): Support variable offset that can be updated as search progress.
58 public:
59 explicit PrecedenceRelations(Model* model)
60 : params_(*model->GetOrCreate<SatParameters>()),
61 trail_(model->GetOrCreate<Trail>()),
62 integer_trail_(model->GetOrCreate<IntegerTrail>()) {
63 integer_trail_->RegisterReversibleClass(this);
64 }
65
66 void Resize(int num_variables) {
67 graph_.ReserveNodes(num_variables);
68 graph_.AddNode(num_variables - 1);
69 }
70
71 // Add a relation lb <= expr <= ub. If expr is not a proper linear2 expression
72 // (e.g. 0*x + y, y + y, y - y) it will be ignored. Returns true if it was
73 // added and is considered "new".
74 bool AddBounds(LinearExpression2 expr, IntegerValue lb, IntegerValue ub);
75
76 // Same as above, but only for the upper bound.
77 bool AddUpperBound(LinearExpression2 expr, IntegerValue ub);
78
79 // Adds add relation (enf => expr <= rhs) that is assumed to be true at
80 // the current level.
81 //
82 // It will be automatically reverted via the SetLevel() functions that is
83 // called before any integer propagations trigger.
84 //
85 // This is assumed to be called when a relation becomes true (enforcement are
86 // assigned) and when it becomes false in reverse order (CHECKed).
87 //
88 // If expr is not a proper linear2 expression (e.g. 0*x + y, y + y, y - y) it
89 // will be ignored.
90 void PushConditionalRelation(absl::Span<const Literal> enforcements,
91 LinearExpression2 expr, IntegerValue rhs);
92
93 // Called each time we change decision level.
94 void SetLevel(int level) final;
95
96 // Returns a set of relations var >= max_i(vars[index[i]] + offsets[i]).
97 //
98 // This currently only works if the precedence relation form a DAG.
99 // If not we will just abort. TODO(user): generalize.
100 //
101 // For more efficiency, this method ignores all linear2 expressions with any
102 // coefficient different from 1.
103 //
104 // TODO(user): Put some work limit in place, as this can be slow. Complexity
105 // is in O(vars.size()) * num_arcs.
106 //
107 // TODO(user): Since we don't need ALL precedences, we could just work on a
108 // sub-DAG of the full precedence graph instead of aborting. Or we can just
109 // support the general non-DAG cases.
110 //
111 // TODO(user): Many relations can be redundant. Filter them.
112 void ComputeFullPrecedences(absl::Span<const IntegerVariable> vars,
113 std::vector<FullIntegerPrecedence>* output);
114
115 // Returns a set of precedences (var, index) such that var is after
116 // vars[index]. All entries for the same variable will be contiguous and
117 // sorted by index. We only list variable with at least two entries. The
118 // offset can be retrieved via UpperBound(vars[index], var).
119 //
120 // For more efficiency, this method ignores all linear2 expressions with any
121 // coefficient different from 1.
123 IntegerVariable var;
124 int index;
125 };
126 void CollectPrecedences(absl::Span<const IntegerVariable> vars,
127 std::vector<PrecedenceData>* output);
128
129 // If we don't have too many variable, we compute the full transitive closure
130 // and can query in O(1) if there is a relation between two variables.
131 // This can be used to optimize some scheduling propagation and reasons.
132 //
133 // Warning: If there are too many, this will NOT contain all relations.
134 //
135 // Returns kMaxIntegerValue if there are none, otherwise return an upper bound
136 // such that expr <= ub.
137 IntegerValue LevelZeroUpperBound(LinearExpression2 expr) const;
138
139 // Returns the maximum value for expr, and the reason for it (all
140 // true). Note that we always check LevelZeroUpperBound() so if it is better,
141 // the returned literal reason will be empty.
142 //
143 // We separate the two because usually the reason is only needed when we push,
144 // which happen less often, so we don't mind doing two hash lookups, and we
145 // really want to optimize the UpperBound() instead.
146 //
147 // Important: This doesn't contains the transitive closure.
148 // Important: The span is only valid in a narrow scope.
149 IntegerValue UpperBound(LinearExpression2 expr) const;
150
152 LinearExpression2 expr, IntegerValue ub,
153 std::vector<Literal>* literal_reason,
154 std::vector<IntegerLiteral>* integer_reason) const;
155
156 // The current code requires the internal data to be processed once all
157 // relations are loaded.
158 //
159 // TODO(user): Be more dynamic as we start to add relations during search.
160 void Build();
161
162 private:
163 void CreateLevelEntryIfNeeded();
164
165 // expr <= ub.
166 bool AddInternal(LinearExpression2 expr, IntegerValue ub) {
168 if (expr.coeffs[0] == 0 || expr.coeffs[1] == 0) {
169 return false;
170 }
171 const auto [it, inserted] = root_relations_.insert({expr, ub});
172 UpdateBestRelationIfBetter(expr, ub);
173 if (inserted) {
174 if (expr.coeffs[0] != 1 || expr.coeffs[1] != 1) {
175 return true;
176 }
177 const int new_size =
178 std::max(expr.vars[0].value(), expr.vars[1].value()) + 1;
179 if (new_size > after_.size()) after_.resize(new_size);
180 after_[expr.vars[0]].push_back(NegationOf(expr.vars[1]));
181 after_[expr.vars[1]].push_back(NegationOf(expr.vars[0]));
182 return true;
183 }
184 it->second = std::min(it->second, ub);
185 return false;
186 }
187
188 void UpdateBestRelationIfBetter(LinearExpression2 expr, IntegerValue rhs) {
189 const auto [it, inserted] = best_relations_.insert({expr, rhs});
190 if (!inserted) {
191 it->second = std::min(it->second, rhs);
192 }
193 }
194
195 void UpdateBestRelation(LinearExpression2 expr, IntegerValue rhs) {
196 const auto it = root_relations_.find(expr);
197 if (it != root_relations_.end()) {
198 rhs = std::min(rhs, it->second);
199 }
200 if (rhs == kMaxIntegerValue) {
201 best_relations_.erase(expr);
202 } else {
203 best_relations_[expr] = rhs;
204 }
205 }
206
207 const SatParameters& params_;
208 Trail* trail_;
209 IntegerTrail* integer_trail_;
210
211 util::StaticGraph<> graph_;
212 std::vector<IntegerValue> arc_offsets_;
213
214 bool is_built_ = false;
215 bool is_dag_ = false;
216 std::vector<IntegerVariable> topological_order_;
217
218 // Conditional stack for push/pop of conditional relations.
219 //
220 // TODO(user): this kind of reversible hash_map is already implemented in
221 // other part of the code. Consolidate.
222 struct ConditionalEntry {
223 ConditionalEntry(int p, IntegerValue r, LinearExpression2 k,
224 absl::Span<const Literal> e)
225 : prev_entry(p), rhs(r), key(k), enforcements(e.begin(), e.end()) {}
226
227 int prev_entry;
228 IntegerValue rhs;
229 LinearExpression2 key;
230 absl::InlinedVector<Literal, 4> enforcements;
231 };
232 std::vector<ConditionalEntry> conditional_stack_;
233 std::vector<std::pair<int, int>> level_to_stack_size_;
234
235 // This is always stored in the form (expr <= rhs).
236 // The conditional relations contains indices in the conditional_stack_.
237 absl::flat_hash_map<LinearExpression2, IntegerValue> root_relations_;
238 absl::flat_hash_map<LinearExpression2, int> conditional_relations_;
239
240 // Contains std::min() of the offset from root_relations_ and
241 // conditional_relations_.
242 absl::flat_hash_map<LinearExpression2, IntegerValue> best_relations_;
243
244 // Store for each variable x, the variables y that appears alongside it in
245 // LevelZeroUpperBound(expr) or UpperBound(expr). That is the variable
246 // that are after x with an offset. Note that conditional_after_ is updated on
247 // dive/backtrack.
248 util_intops::StrongVector<IntegerVariable, std::vector<IntegerVariable>>
249 after_;
250 util_intops::StrongVector<IntegerVariable, std::vector<IntegerVariable>>
251 conditional_after_;
252
253 // Temp data for CollectPrecedences.
254 std::vector<IntegerVariable> var_with_positive_degree_;
255 util_intops::StrongVector<IntegerVariable, int> var_to_degree_;
256 util_intops::StrongVector<IntegerVariable, int> var_to_last_index_;
257 std::vector<PrecedenceData> tmp_precedences_;
258};
259
260// This class implement a propagator on simple inequalities between integer
261// variables of the form (i1 + offset <= i2). The offset can be constant or
262// given by the value of a third integer variable. Offsets can also be negative.
263//
264// The algorithm works by mapping the problem onto a graph where the edges carry
265// the offset and the nodes correspond to one of the two bounds of an integer
266// variable (lower_bound or -upper_bound). It then find the fixed point using an
267// incremental variant of the Bellman-Ford(-Tarjan) algorithm.
268//
269// This is also known as an "integer difference logic theory" in the SMT world.
270// Another word is "separation logic".
271//
272// TODO(user): We could easily generalize the code to support any relation of
273// the form a*X + b*Y + c*Z >= rhs (or <=). Do that since this class should be
274// a lot faster at propagating small linear inequality than the generic
275// propagator and the overhead of supporting coefficient should not be too bad.
277 public:
279 : SatPropagator("PrecedencesPropagator"),
280 relations_(model->GetOrCreate<PrecedenceRelations>()),
281 trail_(model->GetOrCreate<Trail>()),
282 integer_trail_(model->GetOrCreate<IntegerTrail>()),
283 shared_stats_(model->Mutable<SharedStatistics>()),
284 watcher_(model->GetOrCreate<GenericLiteralWatcher>()),
285 watcher_id_(watcher_->Register(this)) {
286 model->GetOrCreate<SatSolver>()->AddPropagator(this);
287 integer_trail_->RegisterWatcher(&modified_vars_);
288 watcher_->SetPropagatorPriority(watcher_id_, 0);
289 }
290
291 // This type is neither copyable nor movable.
294 ~PrecedencesPropagator() override;
295
296 bool Propagate() final;
297 bool Propagate(Trail* trail) final;
298 void Untrail(const Trail& trail, int trail_index) final;
299
300 // Propagates all the outgoing arcs of the given variable (and only those). It
301 // is more efficient to do all these propagation in one go by calling
302 // Propagate(), but for scheduling problem, we wants to propagate right away
303 // the end of an interval when its start moved.
304 bool PropagateOutgoingArcs(IntegerVariable var);
305
306 // Add a precedence relation (i1 + offset <= i2) between integer variables.
307 //
308 // Important: The optionality of the variable should be marked BEFORE this
309 // is called.
310 void AddPrecedence(IntegerVariable i1, IntegerVariable i2);
311 void AddPrecedenceWithOffset(IntegerVariable i1, IntegerVariable i2,
312 IntegerValue offset);
313 void AddPrecedenceWithVariableOffset(IntegerVariable i1, IntegerVariable i2,
314 IntegerVariable offset_var);
315
316 // Same as above, but the relation is only true when the given literal is.
317 void AddConditionalPrecedence(IntegerVariable i1, IntegerVariable i2,
318 Literal l);
319 void AddConditionalPrecedenceWithOffset(IntegerVariable i1,
320 IntegerVariable i2,
321 IntegerValue offset, Literal l);
322
323 // Generic function that cover all of the above case and more.
324 void AddPrecedenceWithAllOptions(IntegerVariable i1, IntegerVariable i2,
325 IntegerValue offset,
326 IntegerVariable offset_var,
327 absl::Span<const Literal> presence_literals);
328
329 // This version check current precedence. It is however "slow".
330 bool AddPrecedenceWithOffsetIfNew(IntegerVariable i1, IntegerVariable i2,
331 IntegerValue offset);
332
333 private:
334 DEFINE_STRONG_INDEX_TYPE(ArcIndex);
335 DEFINE_STRONG_INDEX_TYPE(OptionalArcIndex);
336
337 // Information about an individual arc.
338 struct ArcInfo {
339 IntegerVariable tail_var;
340 IntegerVariable head_var;
341
342 IntegerValue offset;
343 IntegerVariable offset_var; // kNoIntegerVariable if none.
344
345 // This arc is "present" iff all these literals are true.
346 absl::InlinedVector<Literal, 6> presence_literals;
347
348 // Used temporarily by our implementation of the Bellman-Ford algorithm. It
349 // should be false at the beginning of BellmanFordTarjan().
350 mutable bool is_marked;
351 };
352
353 // Internal functions to add new precedence relations.
354 //
355 // Note that internally, we only propagate lower bounds, so each time we add
356 // an arc, we actually create two of them: one on the given variables, and one
357 // on their negation.
358 void AdjustSizeFor(IntegerVariable i);
359 void AddArc(IntegerVariable tail, IntegerVariable head, IntegerValue offset,
360 IntegerVariable offset_var,
361 absl::Span<const Literal> presence_literals);
362
363 // Enqueue a new lower bound for the variable arc.head_lb that was deduced
364 // from the current value of arc.tail_lb and the offset of this arc.
365 bool EnqueueAndCheck(const ArcInfo& arc, IntegerValue new_head_lb,
366 Trail* trail);
367 IntegerValue ArcOffset(const ArcInfo& arc) const;
368
369 // Inspect all the optional arcs that needs inspection (to stay sparse) and
370 // check if their presence literal can be propagated to false.
371 void PropagateOptionalArcs(Trail* trail);
372
373 // The core algorithm implementation is split in these functions. One must
374 // first call InitializeBFQueueWithModifiedNodes() that will push all the
375 // IntegerVariable whose lower bound has been modified since the last call.
376 // Then, BellmanFordTarjan() will take care of all the propagation and returns
377 // false in case of conflict. Internally, it uses DisassembleSubtree() which
378 // is the Tarjan variant to detect a possible positive cycle. Before exiting,
379 // it will call CleanUpMarkedArcsAndParents().
380 //
381 // The Tarjan version of the Bellam-Ford algorithm is really nice in our
382 // context because it was really easy to make it incremental. Moreover, it
383 // supports batch increment!
384 //
385 // This implementation is kind of unique because of our context and the fact
386 // that it is incremental, but a good reference is "Negative-cycle detection
387 // algorithms", Boris V. Cherkassky, Andrew V. Goldberg, 1996,
388 // http://people.cs.nctu.edu.tw/~tjshen/doc/ne.pdf
389 void InitializeBFQueueWithModifiedNodes();
390 bool BellmanFordTarjan(Trail* trail);
391 bool DisassembleSubtree(int source, int target,
392 std::vector<bool>* can_be_skipped);
393 void AnalyzePositiveCycle(ArcIndex first_arc, Trail* trail,
394 std::vector<Literal>* must_be_all_true,
395 std::vector<Literal>* literal_reason,
396 std::vector<IntegerLiteral>* integer_reason);
397 void CleanUpMarkedArcsAndParents();
398
399 // Loops over all the arcs and verify that there is no propagation left.
400 // This is only meant to be used in a DCHECK() and is not optimized.
401 bool NoPropagationLeft(const Trail& trail) const;
402
403 // Update relations_.
404 void PushConditionalRelations(const ArcInfo& arc);
405
406 // External class needed to get the IntegerVariable lower bounds and Enqueue
407 // new ones.
408 PrecedenceRelations* relations_;
409 Trail* trail_;
410 IntegerTrail* integer_trail_;
411 SharedStatistics* shared_stats_ = nullptr;
412 GenericLiteralWatcher* watcher_;
413 int watcher_id_;
414
415 // The key to our incrementality. This will be cleared once the propagation
416 // is done, and automatically updated by the integer_trail_ with all the
417 // IntegerVariable that changed since the last clear.
418 SparseBitset<IntegerVariable> modified_vars_;
419
420 // An arc needs to be inspected for propagation (i.e. is impacted) if its
421 // tail_var changed. If an arc has 3 variables (tail, offset, head), it will
422 // appear as 6 different entries in the arcs_ vector, one for each variable
423 // and its negation, each time with a different tail.
424 //
425 // TODO(user): rearranging the index so that the arc of the same node are
426 // consecutive like in StaticGraph should have a big performance impact.
427 //
428 // TODO(user): We do not need to store ArcInfo.tail_var here.
430 impacted_arcs_;
432
433 // This is similar to impacted_arcs_/arcs_ but it is only used to propagate
434 // one of the presence literals when the arc cannot be present. An arc needs
435 // to appear only once in potential_arcs_, but it will be referenced by
436 // all its variable in impacted_potential_arcs_.
437 util_intops::StrongVector<IntegerVariable,
438 absl::InlinedVector<OptionalArcIndex, 6>>
439 impacted_potential_arcs_;
441
442 // Each time a literal becomes true, this list the set of arcs for which we
443 // need to decrement their count. When an arc count reach zero, it must be
444 // added to the set of impacted_arcs_. Note that counts never becomes
445 // negative.
446 //
447 // TODO(user): Try a one-watcher approach instead. Note that in most cases
448 // arc should be controlled by 1 or 2 literals, so not sure it is worth it.
450 literal_to_new_impacted_arcs_;
452
453 // Temp vectors to hold the reason of an assignment.
454 std::vector<Literal> literal_reason_;
455 std::vector<IntegerLiteral> integer_reason_;
456
457 // Temp vectors for the Bellman-Ford algorithm. The graph in which this
458 // algorithm works is in one to one correspondence with the IntegerVariable in
459 // impacted_arcs_.
460 std::deque<int> bf_queue_;
461 std::vector<bool> bf_in_queue_;
462 std::vector<bool> bf_can_be_skipped_;
463 std::vector<ArcIndex> bf_parent_arc_of_;
464
465 // Temp vector used by the tree traversal in DisassembleSubtree().
466 std::vector<int> tmp_vector_;
467
468 // Stats.
469 int64_t num_cycles_ = 0;
470 int64_t num_pushes_ = 0;
471 int64_t num_enforcement_pushes_ = 0;
472};
473
474// Similar to AffineExpression, but with a zero constant.
475// If coeff is zero, then this is always zero and var is ignored.
477 LinearTerm() = default;
478 LinearTerm(IntegerVariable v, IntegerValue c) : var(v), coeff(c) {}
479
481 if (coeff < 0) {
482 coeff = -coeff;
483 var = NegationOf(var);
484 }
485 }
486
487 bool operator==(const LinearTerm& other) const {
488 return var == other.var && coeff == other.coeff;
489 }
490
491 IntegerVariable var = kNoIntegerVariable;
492 IntegerValue coeff = IntegerValue(0);
493};
494
495// A relation of the form enforcement => a + b \in [lhs, rhs].
496// Note that the [lhs, rhs] interval should always be within [min_activity,
497// max_activity] where the activity is the value of a + b.
498struct Relation {
502 IntegerValue lhs;
503 IntegerValue rhs;
504
505 bool operator==(const Relation& other) const {
506 return enforcement == other.enforcement && a == other.a && b == other.b &&
507 lhs == other.lhs && rhs == other.rhs;
508 }
509};
510
511// A repository of all the enforced linear constraints of size 1 or 2, and of
512// all the non-enforced linear constraints of size 2.
513//
514// TODO(user): This is not always needed, find a way to clean this once we
515// don't need it.
517 public:
518 int size() const { return relations_.size(); }
519
520 // The returned relation is guaranteed to only have positive variables.
521 const Relation& relation(int index) const { return relations_[index]; }
522
523 // Returns the indices of the relations that are enforced by the given
524 // literal.
525 absl::Span<const int> IndicesOfRelationsEnforcedBy(LiteralIndex lit) const {
526 if (lit >= lit_to_relations_.size()) return {};
527 return lit_to_relations_[lit];
528 }
529
530 // Returns the indices of the non-enforced relations that contain the given
531 // (positive) variable.
532 absl::Span<const int> IndicesOfRelationsContaining(
533 IntegerVariable var) const {
534 if (var >= var_to_relations_.size()) return {};
535 return var_to_relations_[var];
536 }
537
538 // Returns the indices of the non-enforced relations that contain the given
539 // (positive) variables.
540 absl::Span<const int> IndicesOfRelationsBetween(IntegerVariable var1,
541 IntegerVariable var2) const {
542 if (var1 > var2) std::swap(var1, var2);
543 const std::pair<IntegerVariable, IntegerVariable> key(var1, var2);
544 const auto it = var_pair_to_relations_.find(key);
545 if (it == var_pair_to_relations_.end()) return {};
546 return it->second;
547 }
548
549 // Adds a conditional relation lit => a + b \in [lhs, rhs] (one of the terms
550 // can be zero), or an always true binary relation a + b \in [lhs, rhs] (both
551 // terms must be non-zero).
552 void Add(Literal lit, LinearTerm a, LinearTerm b, IntegerValue lhs,
553 IntegerValue rhs);
554
555 // Adds a partial conditional relation between two variables, with unspecified
556 // coefficients and bounds.
557 void AddPartialRelation(Literal lit, IntegerVariable a, IntegerVariable b);
558
559 // Builds the literal to relations mapping. This should be called once all the
560 // relations have been added.
561 void Build();
562
563 // Assuming level-zero bounds + any (var >= value) in the input map,
564 // fills "output" with a "propagated" set of bounds assuming lit is true (by
565 // using the relations enforced by lit, as well as the non-enforced ones).
566 // Note that we will only fill bounds > level-zero ones in output.
567 //
568 // Returns false if the new bounds are infeasible at level zero.
569 //
570 // Important: by default this does not call output->clear() so we can take
571 // the max with already inferred bounds.
573 const IntegerTrail& integer_trail, Literal lit,
574 const absl::flat_hash_map<IntegerVariable, IntegerValue>& input,
575 absl::flat_hash_map<IntegerVariable, IntegerValue>* output) const;
576
577 private:
578 bool is_built_ = false;
579 int num_enforced_relations_ = 0;
580 std::vector<Relation> relations_;
583 absl::flat_hash_map<std::pair<IntegerVariable, IntegerVariable>,
584 std::vector<int>>
585 var_pair_to_relations_;
586};
587
588// TODO(user): Merge with BinaryRelationRepository. Note that this one provides
589// different indexing though, so it could be kept separate. The
590// LinearExpression2 data structure is also slightly more efficient.
592 public:
593 explicit BinaryRelationsMaps(Model* model);
595
596 // This mainly wraps BestBinaryRelationBounds, but in addition it checks the
597 // current LevelZero variable bounds to detect trivially true or false
598 // relation.
599 void AddRelationBounds(LinearExpression2 expr, IntegerValue lb,
600 IntegerValue ub);
602 IntegerValue ub) const;
603
604 // Return the status of a <= b;
606 AffineExpression b) const;
607
608 // Register the fact that l <=> ( a <= b ).
609 // These are considered equivalence relation.
612
613 // Returns kNoLiteralIndex if we don't have a literal <=> ( a <= b ), or
614 // returns that literal if we have one. Note that we will return the
615 // true/false literal if the status is known at level zero.
617
618 // If the given upper bound evaluate better than the current one we have, this
619 // will replace it and returns true, otherwise it returns false.
620 //
621 // Note that we never store trivial upper bound (using the current variable
622 // domain).
624
625 // Returns the best known upper-bound of the given LinearExpression2 at the
626 // current decision level. If its explanation is needed, it can be queried
627 // with the second function.
628 IntegerValue UpperBound(LinearExpression2 expr) const;
630 LinearExpression2 expr, IntegerValue ub,
631 std::vector<Literal>* literal_reason,
632 std::vector<IntegerLiteral>* integer_reason) const;
633
634 // Warning, the order will not be deterministic.
635 std::vector<LinearExpression2> GetAllExpressionsWithAffineBounds() const;
636
637 int NumExpressionsWithAffineBounds() const { return best_affine_ub_.size(); }
638
639 void WatchAllLinearExpressions2(int id) { propagator_ids_.insert(id); }
640
641 private:
642 void NotifyWatchingPropagators() const;
643
644 // Return the pair (a - b) <= rhs.
645 std::pair<LinearExpression2, IntegerValue> FromDifference(
646 const AffineExpression& a, const AffineExpression& b) const;
647
648 IntegerValue GetImpliedUpperBound(const LinearExpression2& expr) const;
649 std::pair<IntegerValue, IntegerValue> GetImpliedLevelZeroBounds(
650 const LinearExpression2& expr) const;
651
652 IntegerTrail* integer_trail_;
653 IntegerEncoder* integer_encoder_;
654 GenericLiteralWatcher* watcher_;
655 SharedStatistics* shared_stats_;
656 BestBinaryRelationBounds best_root_level_bounds_;
657
658 int64_t num_updates_ = 0;
659 int64_t num_affine_updates_ = 0;
660
661 // This stores relations l <=> (linear2 <= rhs).
662 absl::flat_hash_map<std::pair<LinearExpression2, IntegerValue>, Literal>
663 relation_to_lit_;
664
665 // This is used to detect relations that become fixed at level zero and
666 // "upgrade" them to non-enforced relations. Because we only do that when
667 // we fix variable, a linear scan shouldn't be too bad and is relatively
668 // compact memory wise.
669 absl::flat_hash_set<BooleanVariable> variable_appearing_in_reified_relations_;
670 std::vector<std::tuple<Literal, LinearExpression2, IntegerValue>>
671 all_reified_relations_;
672
673 // This stores linear2 <= AffineExpression / divisor.
674 //
675 // Note(user): This is a "cheap way" to not have to deal with backtracking, If
676 // we have many possible AffineExpression that bounds a LinearExpression2, we
677 // keep the best one during "search dive" but on backtrack we might have a
678 // sub-optimal relation.
679 absl::flat_hash_map<LinearExpression2,
680 std::pair<AffineExpression, IntegerValue>>
681 best_affine_ub_;
682
683 absl::btree_set<int> propagator_ids_;
684};
685
686// Detects if at least one of a subset of linear of size 2 or 1, touching the
687// same variable, must be true. When this is the case we add a new propagator to
688// propagate that fact.
689//
690// TODO(user): Shall we do that on the main thread before the workers are
691// spawned? note that the probing version need the model to be loaded though.
693 public:
695 : repository_(*model->GetOrCreate<BinaryRelationRepository>()) {}
696
697 // Advanced usage. To be called once all the constraints have been added to
698 // the model. This will detect GreaterThanAtLeastOneOfConstraint().
699 // Returns the number of added constraint.
700 //
701 // TODO(user): This can be quite slow, add some kind of deterministic limit
702 // so that we can use it all the time.
704 bool auto_detect_clauses = false);
705
706 private:
707 // Given an existing clause, sees if it can be used to add "greater than at
708 // least one of" type of constraints. Returns the number of such constraint
709 // added.
710 int AddGreaterThanAtLeastOneOfConstraintsFromClause(
711 absl::Span<const Literal> clause, Model* model);
712
713 // Another approach for AddGreaterThanAtLeastOneOfConstraints(), this one
714 // might be a bit slow as it relies on the propagation engine to detect
715 // clauses between incoming arcs presence literals.
716 // Returns the number of added constraints.
717 int AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(
718 Model* model);
719
720 // Once we identified a clause and relevant indices, this build the
721 // constraint. Returns true if we actually add it.
722 bool AddRelationFromIndices(IntegerVariable var,
723 absl::Span<const Literal> clause,
724 absl::Span<const int> indices, Model* model);
725
726 BinaryRelationRepository& repository_;
727};
728
729// =============================================================================
730// Implementation of the small API functions below.
731// =============================================================================
732
733inline void PrecedencesPropagator::AddPrecedence(IntegerVariable i1,
734 IntegerVariable i2) {
735 AddArc(i1, i2, /*offset=*/IntegerValue(0), /*offset_var=*/kNoIntegerVariable,
736 {});
737}
738
740 IntegerVariable i1, IntegerVariable i2, IntegerValue offset) {
741 AddArc(i1, i2, offset, /*offset_var=*/kNoIntegerVariable, {});
742}
743
744inline void PrecedencesPropagator::AddConditionalPrecedence(IntegerVariable i1,
745 IntegerVariable i2,
747 AddArc(i1, i2, /*offset=*/IntegerValue(0), /*offset_var=*/kNoIntegerVariable,
748 {l});
749}
750
752 IntegerVariable i1, IntegerVariable i2, IntegerValue offset, Literal l) {
753 AddArc(i1, i2, offset, /*offset_var=*/kNoIntegerVariable, {l});
754}
755
757 IntegerVariable i1, IntegerVariable i2, IntegerVariable offset_var) {
758 AddArc(i1, i2, /*offset=*/IntegerValue(0), offset_var, {});
759}
760
762 IntegerVariable i1, IntegerVariable i2, IntegerValue offset,
763 IntegerVariable offset_var, absl::Span<const Literal> presence_literals) {
764 AddArc(i1, i2, offset, offset_var, presence_literals);
765}
766
767// =============================================================================
768// Model based functions.
769// =============================================================================
770
771// a <= b.
772inline std::function<void(Model*)> LowerOrEqual(IntegerVariable a,
773 IntegerVariable b) {
774 return [=](Model* model) {
775 return model->GetOrCreate<PrecedencesPropagator>()->AddPrecedence(a, b);
776 };
777}
778
779// a + offset <= b.
780inline std::function<void(Model*)> LowerOrEqualWithOffset(IntegerVariable a,
781 IntegerVariable b,
782 int64_t offset) {
783 return [=](Model* model) {
784 LinearExpression2 expr(a, b, 1, -1);
785 model->GetOrCreate<PrecedenceRelations>()->AddUpperBound(
786 expr, IntegerValue(-offset));
787 model->GetOrCreate<PrecedencesPropagator>()->AddPrecedenceWithOffset(
788 a, b, IntegerValue(offset));
789 };
790}
791
792// a + offset <= b. (when a and b are of the form 1 * var + offset).
793inline std::function<void(Model*)> AffineCoeffOneLowerOrEqualWithOffset(
794 AffineExpression a, AffineExpression b, int64_t offset) {
795 CHECK_NE(a.var, kNoIntegerVariable);
796 CHECK_EQ(a.coeff, 1);
797 CHECK_NE(b.var, kNoIntegerVariable);
798 CHECK_EQ(b.coeff, 1);
799 return [=](Model* model) {
800 LinearExpression2 expr(a.var, b.var, 1, -1);
801 model->GetOrCreate<PrecedenceRelations>()->AddUpperBound(
802 expr, -a.constant + b.constant - offset);
803 model->GetOrCreate<PrecedencesPropagator>()->AddPrecedenceWithOffset(
804 a.var, b.var, a.constant - b.constant + offset);
805 };
806}
807
808// l => (a + b <= ub).
810 absl::Span<const Literal> enforcement_literals, IntegerVariable a,
811 IntegerVariable b, int64_t ub, Model* model) {
812 // TODO(user): Refactor to be sure we do not miss any level zero relations.
813 if (enforcement_literals.empty()) {
814 LinearExpression2 expr(a, b, 1, 1);
815 model->GetOrCreate<PrecedenceRelations>()->AddUpperBound(expr,
816 IntegerValue(ub));
817 }
818
819 PrecedencesPropagator* p = model->GetOrCreate<PrecedencesPropagator>();
820 p->AddPrecedenceWithAllOptions(a, NegationOf(b), IntegerValue(-ub),
821 kNoIntegerVariable, enforcement_literals);
822}
823
824// l => (a + b + c <= ub).
825//
826// TODO(user): Use level zero bounds to infer binary precedence relations?
828 absl::Span<const Literal> enforcement_literals, IntegerVariable a,
829 IntegerVariable b, IntegerVariable c, int64_t ub, Model* model) {
830 PrecedencesPropagator* p = model->GetOrCreate<PrecedencesPropagator>();
831 p->AddPrecedenceWithAllOptions(a, NegationOf(c), IntegerValue(-ub), b,
832 enforcement_literals);
833}
834
835// a >= b.
836inline std::function<void(Model*)> GreaterOrEqual(IntegerVariable a,
837 IntegerVariable b) {
838 return [=](Model* model) {
839 return model->GetOrCreate<PrecedencesPropagator>()->AddPrecedence(b, a);
840 };
841}
842
843// a == b.
844inline std::function<void(Model*)> Equality(IntegerVariable a,
845 IntegerVariable b) {
846 return [=](Model* model) {
847 model->Add(LowerOrEqual(a, b));
848 model->Add(LowerOrEqual(b, a));
849 };
850}
851
852// a + offset == b.
853inline std::function<void(Model*)> EqualityWithOffset(IntegerVariable a,
854 IntegerVariable b,
855 int64_t offset) {
856 return [=](Model* model) {
857 model->Add(LowerOrEqualWithOffset(a, b, offset));
858 model->Add(LowerOrEqualWithOffset(b, a, -offset));
859 };
860}
861
862// is_le => (a + offset <= b).
863inline std::function<void(Model*)> ConditionalLowerOrEqualWithOffset(
864 IntegerVariable a, IntegerVariable b, int64_t offset, Literal is_le) {
865 return [=](Model* model) {
866 PrecedencesPropagator* p = model->GetOrCreate<PrecedencesPropagator>();
867 p->AddConditionalPrecedenceWithOffset(a, b, IntegerValue(offset), is_le);
868 };
869}
870
871} // namespace sat
872} // namespace operations_research
873
874#endif // OR_TOOLS_SAT_PRECEDENCES_H_
Definition model.h:341
absl::Span< const int > IndicesOfRelationsBetween(IntegerVariable var1, IntegerVariable var2) const
absl::Span< const int > IndicesOfRelationsContaining(IntegerVariable var) const
absl::Span< const int > IndicesOfRelationsEnforcedBy(LiteralIndex lit) const
bool PropagateLocalBounds(const IntegerTrail &integer_trail, Literal lit, const absl::flat_hash_map< IntegerVariable, IntegerValue > &input, absl::flat_hash_map< IntegerVariable, IntegerValue > *output) const
const Relation & relation(int index) const
The returned relation is guaranteed to only have positive variables.
void AddPartialRelation(Literal lit, IntegerVariable a, IntegerVariable b)
void Add(Literal lit, LinearTerm a, LinearTerm b, IntegerValue lhs, IntegerValue rhs)
void AddReifiedPrecedenceIfNonTrivial(Literal l, AffineExpression a, AffineExpression b)
IntegerValue UpperBound(LinearExpression2 expr) const
std::vector< LinearExpression2 > GetAllExpressionsWithAffineBounds() const
Warning, the order will not be deterministic.
RelationStatus GetLevelZeroPrecedenceStatus(AffineExpression a, AffineExpression b) const
Return the status of a <= b;.
LiteralIndex GetReifiedPrecedence(AffineExpression a, AffineExpression b)
void AddReasonForUpperBoundLowerThan(LinearExpression2 expr, IntegerValue ub, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason) const
RelationStatus GetLevelZeroStatus(LinearExpression2 expr, IntegerValue lb, IntegerValue ub) const
bool AddAffineUpperBound(LinearExpression2 expr, AffineExpression affine_ub)
void AddRelationBounds(LinearExpression2 expr, IntegerValue lb, IntegerValue ub)
int AddGreaterThanAtLeastOneOfConstraints(Model *model, bool auto_detect_clauses=false)
void CollectPrecedences(absl::Span< const IntegerVariable > vars, std::vector< PrecedenceData > *output)
bool AddBounds(LinearExpression2 expr, IntegerValue lb, IntegerValue ub)
IntegerValue UpperBound(LinearExpression2 expr) const
void PushConditionalRelation(absl::Span< const Literal > enforcements, LinearExpression2 expr, IntegerValue rhs)
IntegerValue LevelZeroUpperBound(LinearExpression2 expr) const
void ComputeFullPrecedences(absl::Span< const IntegerVariable > vars, std::vector< FullIntegerPrecedence > *output)
bool AddUpperBound(LinearExpression2 expr, IntegerValue ub)
Same as above, but only for the upper bound.
void SetLevel(int level) final
Called each time we change decision level.
void AddReasonForUpperBoundLowerThan(LinearExpression2 expr, IntegerValue ub, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason) const
bool AddPrecedenceWithOffsetIfNew(IntegerVariable i1, IntegerVariable i2, IntegerValue offset)
This version check current precedence. It is however "slow".
PrecedencesPropagator(const PrecedencesPropagator &)=delete
This type is neither copyable nor movable.
void AddPrecedence(IntegerVariable i1, IntegerVariable i2)
void AddPrecedenceWithOffset(IntegerVariable i1, IntegerVariable i2, IntegerValue offset)
void AddPrecedenceWithAllOptions(IntegerVariable i1, IntegerVariable i2, IntegerValue offset, IntegerVariable offset_var, absl::Span< const Literal > presence_literals)
Generic function that cover all of the above case and more.
void AddPrecedenceWithVariableOffset(IntegerVariable i1, IntegerVariable i2, IntegerVariable offset_var)
PrecedencesPropagator & operator=(const PrecedencesPropagator &)=delete
void AddConditionalPrecedenceWithOffset(IntegerVariable i1, IntegerVariable i2, IntegerValue offset, Literal l)
void Untrail(const Trail &trail, int trail_index) final
void AddConditionalPrecedence(IntegerVariable i1, IntegerVariable i2, Literal l)
Same as above, but the relation is only true when the given literal is.
SatPropagator(const std::string &name)
Definition sat_base.h:535
void AddPropagator(SatPropagator *propagator)
Simple class to add statistics by name and print them at the end.
void push_back(const value_type &val)
void resize(size_type new_size)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::function< void(Model *)> Equality(IntegerVariable v, int64_t value)
Fix v to a given value.
Definition integer.h:1638
std::function< void(Model *)> LowerOrEqualWithOffset(IntegerVariable a, IntegerVariable b, int64_t offset)
a + offset <= b.
std::function< void(Model *)> AffineCoeffOneLowerOrEqualWithOffset(AffineExpression a, AffineExpression b, int64_t offset)
a + offset <= b. (when a and b are of the form 1 * var + offset).
std::function< void(Model *)> LowerOrEqual(IntegerVariable v, int64_t ub)
Definition integer.h:1624
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
Returns the vector of the negated variables.
Definition integer.cc:52
const IntegerVariable kNoIntegerVariable(-1)
std::function< void(Model *)> EqualityWithOffset(IntegerVariable a, IntegerVariable b, int64_t offset)
a + offset == b.
std::function< void(Model *)> GreaterOrEqual(IntegerVariable v, int64_t lb)
Definition integer.h:1609
void AddConditionalSum3LowerOrEqual(absl::Span< const Literal > enforcement_literals, IntegerVariable a, IntegerVariable b, IntegerVariable c, int64_t ub, Model *model)
void AddConditionalSum2LowerOrEqual(absl::Span< const Literal > enforcement_literals, IntegerVariable a, IntegerVariable b, int64_t ub, Model *model)
l => (a + b <= ub).
std::function< void(Model *)> ConditionalLowerOrEqualWithOffset(IntegerVariable a, IntegerVariable b, int64_t offset, Literal is_le)
is_le => (a + offset <= b).
In SWIG mode, we don't want anything besides these top-level includes.
ClosedInterval::Iterator end(ClosedInterval interval)
ClosedInterval::Iterator begin(ClosedInterval interval)
static int input(yyscan_t yyscanner)
LinearTerm(IntegerVariable v, IntegerValue c)
bool operator==(const LinearTerm &other) const
bool operator==(const Relation &other) const