Google OR-Tools v9.11
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-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_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/inlined_vector.h"
25#include "absl/log/check.h"
26#include "absl/strings/string_view.h"
27#include "absl/types/span.h"
29#include "ortools/base/types.h"
30#include "ortools/graph/graph.h"
31#include "ortools/sat/integer.h"
32#include "ortools/sat/model.h"
36#include "ortools/util/bitset.h"
38
39namespace operations_research {
40namespace sat {
41
43 IntegerVariable var;
44 std::vector<int> indices;
45 std::vector<IntegerValue> offsets;
46};
47
48// Stores all the precedences relation of the form "tail_X + offset <= head_X"
49// that we could extract from the linear constraint of the model. These are
50// stored in a directed graph.
51//
52// TODO(user): Support conditional relation.
53// TODO(user): Support non-DAG like graph.
54// TODO(user): Support variable offset that can be updated as search progress.
56 public:
58 : params_(*model->GetOrCreate<SatParameters>()),
59 trail_(model->GetOrCreate<Trail>()),
60 integer_trail_(model->GetOrCreate<IntegerTrail>()) {
61 integer_trail_->RegisterReversibleClass(this);
62 }
63
64 void Resize(int num_variables) {
65 graph_.ReserveNodes(num_variables);
66 graph_.AddNode(num_variables - 1);
67 }
68
69 // Add a relation tail + offset <= head.
70 // Returns true if it was added and is considered "new".
71 bool Add(IntegerVariable tail, IntegerVariable head, IntegerValue offset);
72
73 // Adds add relation (enf => a + b <= rhs) that is assumed to be true at
74 // the current level.
75 //
76 // It will be automatically reverted via the SetLevel() functions that is
77 // called before any integer propagations trigger.
78 //
79 // This is assumed to be called when a relation becomes true (enforcement are
80 // assigned) and when it becomes false in reverse order (CHECKed).
81 void PushConditionalRelation(absl::Span<const Literal> enforcements,
82 IntegerVariable a, IntegerVariable b,
83 IntegerValue rhs);
84
85 // Called each time we change decision level.
86 void SetLevel(int level) final;
87
88 // Returns a set of relations var >= max_i(vars[index[i]] + offsets[i]).
89 //
90 // This currently only works if the precedence relation form a DAG.
91 // If not we will just abort. TODO(user): generalize.
92 //
93 // TODO(user): Put some work limit in place, as this can be slow. Complexity
94 // is in O(vars.size()) * num_arcs.
95 //
96 // TODO(user): Since we don't need ALL precedences, we could just work on a
97 // sub-DAG of the full precedence graph instead of aborting. Or we can just
98 // support the general non-DAG cases.
99 //
100 // TODO(user): Many relations can be redundant. Filter them.
101 void ComputeFullPrecedences(absl::Span<const IntegerVariable> vars,
102 std::vector<FullIntegerPrecedence>* output);
103
104 // Returns a set of precedences (var, index) such that var is after
105 // vars[index]. All entries for the same variable will be contiguous and
106 // sorted by index. We only list variable with at least two entries. The
107 // offset can be retrieved via GetConditionalOffset(vars[index], var).
109 IntegerVariable var;
110 int index;
111 };
112 void CollectPrecedences(absl::Span<const IntegerVariable> vars,
113 std::vector<PrecedenceData>* output);
114
115 // If we don't have too many variable, we compute the full transitive closure
116 // and can query in O(1) if there is a relation between two variables.
117 // This can be used to optimize some scheduling propagation and reasons.
118 //
119 // Warning: If there are too many, this will NOT contain all relations.
120 //
121 // Returns kMinIntegerValue if there are none.
122 // Otherwise a + offset <= b.
123 IntegerValue GetOffset(IntegerVariable a, IntegerVariable b) const;
124
125 // Returns the minimum distance between a and b, and the reason for it (all
126 // true). Note that we always check GetOffset() so if it is better, the
127 // returned literal reason will be empty.
128 //
129 // We separate the two because usually the reason is only needed when we push,
130 // which happen less often, so we don't mind doing two hash lookups, and we
131 // really want to optimize the GetConditionalOffset() instead.
132 //
133 // Important: This doesn't contains the transitive closure.
134 // Important: The span is only valid in a narrow scope.
135 IntegerValue GetConditionalOffset(IntegerVariable a, IntegerVariable b) const;
136 absl::Span<const Literal> GetConditionalEnforcements(IntegerVariable a,
137 IntegerVariable b) const;
138
139 // The current code requires the internal data to be processed once all
140 // relations are loaded.
141 //
142 // TODO(user): Be more dynamic as we start to add relations during search.
143 void Build();
144
145 private:
146 void CreateLevelEntryIfNeeded();
147
148 std::pair<IntegerVariable, IntegerVariable> GetKey(IntegerVariable a,
149 IntegerVariable b) const {
150 return a <= b ? std::make_pair(a, b) : std::make_pair(b, a);
151 }
152
153 // tail + offset <= head.
154 // Which is the same as tail - head <= -offset.
155 bool AddInternal(IntegerVariable tail, IntegerVariable head,
156 IntegerValue offset) {
157 const auto key = GetKey(tail, NegationOf(head));
158 const auto [it, inserted] = root_relations_.insert({key, -offset});
159 UpdateBestRelationIfBetter(key, -offset);
160 if (inserted) {
161 const int new_size = std::max(tail.value(), NegationOf(head).value()) + 1;
162 if (new_size > after_.size()) after_.resize(new_size);
163 after_[tail].push_back(head);
165 return true;
166 }
167 it->second = std::min(it->second, -offset);
168 return false;
169 }
170
171 void UpdateBestRelationIfBetter(
172 std::pair<IntegerVariable, IntegerVariable> key, IntegerValue rhs) {
173 const auto [it, inserted] = best_relations_.insert({key, rhs});
174 if (!inserted) {
175 it->second = std::min(it->second, rhs);
176 }
177 }
178
179 void UpdateBestRelation(std::pair<IntegerVariable, IntegerVariable> key,
180 IntegerValue rhs) {
181 const auto it = root_relations_.find(key);
182 if (it != root_relations_.end()) {
183 rhs = std::min(rhs, it->second);
184 }
185 if (rhs == kMaxIntegerValue) {
186 best_relations_.erase(key);
187 } else {
188 best_relations_[key] = rhs;
189 }
190 }
191
192 const SatParameters& params_;
193 Trail* trail_;
194 IntegerTrail* integer_trail_;
195
196 util::StaticGraph<> graph_;
197 std::vector<IntegerValue> arc_offsets_;
198
199 bool is_built_ = false;
200 bool is_dag_ = false;
201 std::vector<IntegerVariable> topological_order_;
202
203 // Conditional stack for push/pop of conditional relations.
204 //
205 // TODO(user): this kind of reversible hash_map is already implemented in
206 // other part of the code. Consolidate.
207 struct ConditionalEntry {
208 ConditionalEntry(int p, IntegerValue r,
209 std::pair<IntegerVariable, IntegerVariable> k,
210 absl::Span<const Literal> e)
211 : prev_entry(p), rhs(r), key(k), enforcements(e.begin(), e.end()) {}
212
213 int prev_entry;
214 IntegerValue rhs;
215 std::pair<IntegerVariable, IntegerVariable> key;
216 absl::InlinedVector<Literal, 4> enforcements;
217 };
218 std::vector<ConditionalEntry> conditional_stack_;
219 std::vector<std::pair<int, int>> level_to_stack_size_;
220
221 // This is always stored in the form (a + b <= rhs).
222 // The conditional relations contains indices in the conditional_stack_.
223 absl::flat_hash_map<std::pair<IntegerVariable, IntegerVariable>, IntegerValue>
224 root_relations_;
225 absl::flat_hash_map<std::pair<IntegerVariable, IntegerVariable>, int>
226 conditional_relations_;
227
228 // Contains std::min() of the offset from root_relations_ and
229 // conditional_relations_.
230 absl::flat_hash_map<std::pair<IntegerVariable, IntegerVariable>, IntegerValue>
231 best_relations_;
232
233 // Store for each variable x, the variables y that appears in GetOffset(x, y)
234 // or GetConditionalOffset(x, y). That is the variable that are after x with
235 // an offset. Note that conditional_after_ is updated on dive/backtrack.
237 after_;
239 conditional_after_;
240
241 // Temp data for CollectPrecedences.
242 std::vector<IntegerVariable> var_with_positive_degree_;
245 std::vector<PrecedenceData> tmp_precedences_;
246};
247
248// This class implement a propagator on simple inequalities between integer
249// variables of the form (i1 + offset <= i2). The offset can be constant or
250// given by the value of a third integer variable. Offsets can also be negative.
251//
252// The algorithm works by mapping the problem onto a graph where the edges carry
253// the offset and the nodes correspond to one of the two bounds of an integer
254// variable (lower_bound or -upper_bound). It then find the fixed point using an
255// incremental variant of the Bellman-Ford(-Tarjan) algorithm.
256//
257// This is also known as an "integer difference logic theory" in the SMT world.
258// Another word is "separation logic".
259//
260// TODO(user): We could easily generalize the code to support any relation of
261// the form a*X + b*Y + c*Z >= rhs (or <=). Do that since this class should be
262// a lot faster at propagating small linear inequality than the generic
263// propagator and the overhead of supporting coefficient should not be too bad.
265 public:
267 : SatPropagator("PrecedencesPropagator"),
268 relations_(model->GetOrCreate<PrecedenceRelations>()),
269 trail_(model->GetOrCreate<Trail>()),
270 integer_trail_(model->GetOrCreate<IntegerTrail>()),
271 shared_stats_(model->Mutable<SharedStatistics>()),
272 watcher_(model->GetOrCreate<GenericLiteralWatcher>()),
273 watcher_id_(watcher_->Register(this)) {
274 model->GetOrCreate<SatSolver>()->AddPropagator(this);
275 integer_trail_->RegisterWatcher(&modified_vars_);
276 watcher_->SetPropagatorPriority(watcher_id_, 0);
277 }
278
279 // This type is neither copyable nor movable.
282 ~PrecedencesPropagator() override;
283
284 bool Propagate() final;
285 bool Propagate(Trail* trail) final;
286 void Untrail(const Trail& trail, int trail_index) final;
287
288 // Propagates all the outgoing arcs of the given variable (and only those). It
289 // is more efficient to do all these propagation in one go by calling
290 // Propagate(), but for scheduling problem, we wants to propagate right away
291 // the end of an interval when its start moved.
292 bool PropagateOutgoingArcs(IntegerVariable var);
293
294 // Add a precedence relation (i1 + offset <= i2) between integer variables.
295 //
296 // Important: The optionality of the variable should be marked BEFORE this
297 // is called.
298 void AddPrecedence(IntegerVariable i1, IntegerVariable i2);
299 void AddPrecedenceWithOffset(IntegerVariable i1, IntegerVariable i2,
300 IntegerValue offset);
301 void AddPrecedenceWithVariableOffset(IntegerVariable i1, IntegerVariable i2,
302 IntegerVariable offset_var);
303
304 // Same as above, but the relation is only true when the given literal is.
305 void AddConditionalPrecedence(IntegerVariable i1, IntegerVariable i2,
306 Literal l);
307 void AddConditionalPrecedenceWithOffset(IntegerVariable i1,
308 IntegerVariable i2,
309 IntegerValue offset, Literal l);
310
311 // Generic function that cover all of the above case and more.
312 void AddPrecedenceWithAllOptions(IntegerVariable i1, IntegerVariable i2,
313 IntegerValue offset,
314 IntegerVariable offset_var,
315 absl::Span<const Literal> presence_literals);
316
317 // This version check current precedence. It is however "slow".
318 bool AddPrecedenceWithOffsetIfNew(IntegerVariable i1, IntegerVariable i2,
319 IntegerValue offset);
320
321 private:
323 DEFINE_STRONG_INDEX_TYPE(OptionalArcIndex);
324
325 // Information about an individual arc.
326 struct ArcInfo {
327 IntegerVariable tail_var;
328 IntegerVariable head_var;
329
330 IntegerValue offset;
331 IntegerVariable offset_var; // kNoIntegerVariable if none.
332
333 // This arc is "present" iff all these literals are true.
334 absl::InlinedVector<Literal, 6> presence_literals;
335
336 // Used temporarily by our implementation of the Bellman-Ford algorithm. It
337 // should be false at the beginning of BellmanFordTarjan().
338 mutable bool is_marked;
339 };
340
341 // Internal functions to add new precedence relations.
342 //
343 // Note that internally, we only propagate lower bounds, so each time we add
344 // an arc, we actually create two of them: one on the given variables, and one
345 // on their negation.
346 void AdjustSizeFor(IntegerVariable i);
347 void AddArc(IntegerVariable tail, IntegerVariable head, IntegerValue offset,
348 IntegerVariable offset_var,
349 absl::Span<const Literal> presence_literals);
350
351 // Enqueue a new lower bound for the variable arc.head_lb that was deduced
352 // from the current value of arc.tail_lb and the offset of this arc.
353 bool EnqueueAndCheck(const ArcInfo& arc, IntegerValue new_head_lb,
354 Trail* trail);
355 IntegerValue ArcOffset(const ArcInfo& arc) const;
356
357 // Inspect all the optional arcs that needs inspection (to stay sparse) and
358 // check if their presence literal can be propagated to false.
359 void PropagateOptionalArcs(Trail* trail);
360
361 // The core algorithm implementation is split in these functions. One must
362 // first call InitializeBFQueueWithModifiedNodes() that will push all the
363 // IntegerVariable whose lower bound has been modified since the last call.
364 // Then, BellmanFordTarjan() will take care of all the propagation and returns
365 // false in case of conflict. Internally, it uses DisassembleSubtree() which
366 // is the Tarjan variant to detect a possible positive cycle. Before exiting,
367 // it will call CleanUpMarkedArcsAndParents().
368 //
369 // The Tarjan version of the Bellam-Ford algorithm is really nice in our
370 // context because it was really easy to make it incremental. Moreover, it
371 // supports batch increment!
372 //
373 // This implementation is kind of unique because of our context and the fact
374 // that it is incremental, but a good reference is "Negative-cycle detection
375 // algorithms", Boris V. Cherkassky, Andrew V. Goldberg, 1996,
376 // http://people.cs.nctu.edu.tw/~tjshen/doc/ne.pdf
377 void InitializeBFQueueWithModifiedNodes();
378 bool BellmanFordTarjan(Trail* trail);
379 bool DisassembleSubtree(int source, int target,
380 std::vector<bool>* can_be_skipped);
381 void AnalyzePositiveCycle(ArcIndex first_arc, Trail* trail,
382 std::vector<Literal>* must_be_all_true,
383 std::vector<Literal>* literal_reason,
384 std::vector<IntegerLiteral>* integer_reason);
385 void CleanUpMarkedArcsAndParents();
386
387 // Loops over all the arcs and verify that there is no propagation left.
388 // This is only meant to be used in a DCHECK() and is not optimized.
389 bool NoPropagationLeft(const Trail& trail) const;
390
391 // Update relations_.
392 void PushConditionalRelations(const ArcInfo& arc);
393
394 // External class needed to get the IntegerVariable lower bounds and Enqueue
395 // new ones.
396 PrecedenceRelations* relations_;
397 Trail* trail_;
398 IntegerTrail* integer_trail_;
399 SharedStatistics* shared_stats_ = nullptr;
400 GenericLiteralWatcher* watcher_;
401 int watcher_id_;
402
403 // The key to our incrementality. This will be cleared once the propagation
404 // is done, and automatically updated by the integer_trail_ with all the
405 // IntegerVariable that changed since the last clear.
406 SparseBitset<IntegerVariable> modified_vars_;
407
408 // An arc needs to be inspected for propagation (i.e. is impacted) if its
409 // tail_var changed. If an arc has 3 variables (tail, offset, head), it will
410 // appear as 6 different entries in the arcs_ vector, one for each variable
411 // and its negation, each time with a different tail.
412 //
413 // TODO(user): rearranging the index so that the arc of the same node are
414 // consecutive like in StaticGraph should have a big performance impact.
415 //
416 // TODO(user): We do not need to store ArcInfo.tail_var here.
418 impacted_arcs_;
420
421 // This is similar to impacted_arcs_/arcs_ but it is only used to propagate
422 // one of the presence literals when the arc cannot be present. An arc needs
423 // to appear only once in potential_arcs_, but it will be referenced by
424 // all its variable in impacted_potential_arcs_.
425 util_intops::StrongVector<IntegerVariable,
426 absl::InlinedVector<OptionalArcIndex, 6>>
427 impacted_potential_arcs_;
429
430 // Each time a literal becomes true, this list the set of arcs for which we
431 // need to decrement their count. When an arc count reach zero, it must be
432 // added to the set of impacted_arcs_. Note that counts never becomes
433 // negative.
434 //
435 // TODO(user): Try a one-watcher approach instead. Note that in most cases
436 // arc should be controlled by 1 or 2 literals, so not sure it is worth it.
438 literal_to_new_impacted_arcs_;
440
441 // Temp vectors to hold the reason of an assignment.
442 std::vector<Literal> literal_reason_;
443 std::vector<IntegerLiteral> integer_reason_;
444
445 // Temp vectors for the Bellman-Ford algorithm. The graph in which this
446 // algorithm works is in one to one correspondence with the IntegerVariable in
447 // impacted_arcs_.
448 std::deque<int> bf_queue_;
449 std::vector<bool> bf_in_queue_;
450 std::vector<bool> bf_can_be_skipped_;
451 std::vector<ArcIndex> bf_parent_arc_of_;
452
453 // Temp vector used by the tree traversal in DisassembleSubtree().
454 std::vector<int> tmp_vector_;
455
456 // Stats.
457 int64_t num_cycles_ = 0;
458 int64_t num_pushes_ = 0;
459 int64_t num_enforcement_pushes_ = 0;
460};
461
462// Similar to AffineExpression, but with a zero constant.
463// If coeff is zero, then this is always zero and var is ignored.
465 IntegerVariable var = kNoIntegerVariable;
466 IntegerValue coeff = IntegerValue(0);
467};
468
469// This collect all enforced linear of size 2 or 1 and detect if at least one of
470// a subset touching the same variable must be true. When this is the case
471// we add a new propagator to propagate that fact.
472//
473// TODO(user): Shall we do that on the main thread before the workers are
474// spawned? note that the probing version need the model to be loaded though.
476 public:
477 // Adds a relation lit => a + b \in [lhs, rhs].
478 void Add(Literal lit, LinearTerm a, LinearTerm b, IntegerValue lhs,
479 IntegerValue rhs);
480
481 // Advanced usage. To be called once all the constraints have been added to
482 // the model. This will detect GreaterThanAtLeastOneOfConstraint().
483 // Returns the number of added constraint.
484 //
485 // TODO(user): This can be quite slow, add some kind of deterministic limit
486 // so that we can use it all the time.
488 bool auto_detect_clauses = false);
489
490 private:
491 // Given an existing clause, sees if it can be used to add "greater than at
492 // least one of" type of constraints. Returns the number of such constraint
493 // added.
494 int AddGreaterThanAtLeastOneOfConstraintsFromClause(
495 absl::Span<const Literal> clause, Model* model);
496
497 // Another approach for AddGreaterThanAtLeastOneOfConstraints(), this one
498 // might be a bit slow as it relies on the propagation engine to detect
499 // clauses between incoming arcs presence literals.
500 // Returns the number of added constraints.
501 int AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(
502 Model* model);
503
504 // Once we identified a clause and relevant indices, this build the
505 // constraint. Returns true if we actually add it.
506 bool AddRelationFromIndices(IntegerVariable var,
507 absl::Span<const Literal> clause,
508 absl::Span<const int> indices, Model* model);
509
510 struct Relation {
511 Literal enforcement;
514 IntegerValue lhs;
515 IntegerValue rhs;
516 };
517 std::vector<Relation> relations_;
518
519 std::unique_ptr<CompactVectorVector<LiteralIndex, int>> lit_to_relations_;
520};
521
522// =============================================================================
523// Implementation of the small API functions below.
524// =============================================================================
525
526inline void PrecedencesPropagator::AddPrecedence(IntegerVariable i1,
527 IntegerVariable i2) {
528 AddArc(i1, i2, /*offset=*/IntegerValue(0), /*offset_var=*/kNoIntegerVariable,
529 {});
530}
531
533 IntegerVariable i1, IntegerVariable i2, IntegerValue offset) {
534 AddArc(i1, i2, offset, /*offset_var=*/kNoIntegerVariable, {});
535}
536
537inline void PrecedencesPropagator::AddConditionalPrecedence(IntegerVariable i1,
538 IntegerVariable i2,
540 AddArc(i1, i2, /*offset=*/IntegerValue(0), /*offset_var=*/kNoIntegerVariable,
541 {l});
542}
543
545 IntegerVariable i1, IntegerVariable i2, IntegerValue offset, Literal l) {
546 AddArc(i1, i2, offset, /*offset_var=*/kNoIntegerVariable, {l});
547}
548
550 IntegerVariable i1, IntegerVariable i2, IntegerVariable offset_var) {
551 AddArc(i1, i2, /*offset=*/IntegerValue(0), offset_var, {});
552}
553
555 IntegerVariable i1, IntegerVariable i2, IntegerValue offset,
556 IntegerVariable offset_var, absl::Span<const Literal> presence_literals) {
557 AddArc(i1, i2, offset, offset_var, presence_literals);
558}
559
560// =============================================================================
561// Model based functions.
562// =============================================================================
563
564// a <= b.
565inline std::function<void(Model*)> LowerOrEqual(IntegerVariable a,
566 IntegerVariable b) {
567 return [=](Model* model) {
568 return model->GetOrCreate<PrecedencesPropagator>()->AddPrecedence(a, b);
569 };
570}
571
572// a + offset <= b.
573inline std::function<void(Model*)> LowerOrEqualWithOffset(IntegerVariable a,
574 IntegerVariable b,
575 int64_t offset) {
576 return [=](Model* model) {
577 model->GetOrCreate<PrecedenceRelations>()->Add(a, b, IntegerValue(offset));
578 model->GetOrCreate<PrecedencesPropagator>()->AddPrecedenceWithOffset(
579 a, b, IntegerValue(offset));
580 };
581}
582
583// a + offset <= b. (when a and b are of the form 1 * var + offset).
584inline std::function<void(Model*)> AffineCoeffOneLowerOrEqualWithOffset(
585 AffineExpression a, AffineExpression b, int64_t offset) {
586 CHECK_NE(a.var, kNoIntegerVariable);
587 CHECK_EQ(a.coeff, 1);
588 CHECK_NE(b.var, kNoIntegerVariable);
589 CHECK_EQ(b.coeff, 1);
590 return [=](Model* model) {
591 model->GetOrCreate<PrecedenceRelations>()->Add(
592 a.var, b.var, a.constant - b.constant + offset);
593 model->GetOrCreate<PrecedencesPropagator>()->AddPrecedenceWithOffset(
594 a.var, b.var, a.constant - b.constant + offset);
595 };
596}
597
598// l => (a + b <= ub).
600 absl::Span<const Literal> enforcement_literals, IntegerVariable a,
601 IntegerVariable b, int64_t ub, Model* model) {
602 // TODO(user): Refactor to be sure we do not miss any level zero relations.
603 if (enforcement_literals.empty()) {
604 model->GetOrCreate<PrecedenceRelations>()->Add(a, NegationOf(b),
605 IntegerValue(-ub));
606 }
607
608 PrecedencesPropagator* p = model->GetOrCreate<PrecedencesPropagator>();
609 p->AddPrecedenceWithAllOptions(a, NegationOf(b), IntegerValue(-ub),
610 kNoIntegerVariable, enforcement_literals);
611}
612
613// l => (a + b + c <= ub).
614//
615// TODO(user): Use level zero bounds to infer binary precedence relations?
617 absl::Span<const Literal> enforcement_literals, IntegerVariable a,
618 IntegerVariable b, IntegerVariable c, int64_t ub, Model* model) {
619 PrecedencesPropagator* p = model->GetOrCreate<PrecedencesPropagator>();
620 p->AddPrecedenceWithAllOptions(a, NegationOf(c), IntegerValue(-ub), b,
621 enforcement_literals);
622}
623
624// a >= b.
625inline std::function<void(Model*)> GreaterOrEqual(IntegerVariable a,
626 IntegerVariable b) {
627 return [=](Model* model) {
628 return model->GetOrCreate<PrecedencesPropagator>()->AddPrecedence(b, a);
629 };
630}
631
632// a == b.
633inline std::function<void(Model*)> Equality(IntegerVariable a,
634 IntegerVariable b) {
635 return [=](Model* model) {
636 model->Add(LowerOrEqual(a, b));
638 };
639}
640
641// a + offset == b.
642inline std::function<void(Model*)> EqualityWithOffset(IntegerVariable a,
643 IntegerVariable b,
644 int64_t offset) {
645 return [=](Model* model) {
647 model->Add(LowerOrEqualWithOffset(b, a, -offset));
648 };
649}
650
651// is_le => (a + offset <= b).
652inline std::function<void(Model*)> ConditionalLowerOrEqualWithOffset(
653 IntegerVariable a, IntegerVariable b, int64_t offset, Literal is_le) {
654 return [=](Model* model) {
655 PrecedencesPropagator* p = model->GetOrCreate<PrecedencesPropagator>();
656 p->AddConditionalPrecedenceWithOffset(a, b, IntegerValue(offset), is_le);
657 };
658}
659
660} // namespace sat
661} // namespace operations_research
662
663#endif // OR_TOOLS_SAT_PRECEDENCES_H_
void SetPropagatorPriority(int id, int priority)
Definition integer.cc:2358
int AddGreaterThanAtLeastOneOfConstraints(Model *model, bool auto_detect_clauses=false)
void Add(Literal lit, LinearTerm a, LinearTerm b, IntegerValue lhs, IntegerValue rhs)
Adds a relation lit => a + b \in [lhs, rhs].
void RegisterWatcher(SparseBitset< IntegerVariable > *p)
Definition integer.h:1069
void RegisterReversibleClass(ReversibleInterface *rev)
Definition integer.h:1099
void PushConditionalRelation(absl::Span< const Literal > enforcements, IntegerVariable a, IntegerVariable b, IntegerValue rhs)
void CollectPrecedences(absl::Span< const IntegerVariable > vars, std::vector< PrecedenceData > *output)
bool Add(IntegerVariable tail, IntegerVariable head, IntegerValue offset)
IntegerValue GetConditionalOffset(IntegerVariable a, IntegerVariable b) const
void ComputeFullPrecedences(absl::Span< const IntegerVariable > vars, std::vector< FullIntegerPrecedence > *output)
void SetLevel(int level) final
Called each time we change decision level.
IntegerValue GetOffset(IntegerVariable a, IntegerVariable b) const
absl::Span< const Literal > GetConditionalEnforcements(IntegerVariable a, IntegerVariable b) 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.
Base class for CP like propagators.
Definition integer.h:1414
Base class for all the SAT constraints.
Definition sat_base.h:533
void AddPropagator(SatPropagator *propagator)
Simple class to add statistics by name and print them at the end.
void ReserveNodes(NodeIndexType bound) override
Definition graph.h:1308
void AddNode(NodeIndexType node)
Definition graph.h:1324
void push_back(const value_type &val)
void resize(size_type new_size)
int64_t b
Definition table.cc:45
int64_t a
Definition table.cc:44
int64_t value
IntVar * var
GRBmodel * model
int lit
int arc
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:2012
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::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Returns the vector of the negated variables.
Definition integer.cc:51
std::function< void(Model *)> LowerOrEqual(IntegerVariable v, int64_t ub)
Definition integer.h:1998
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:1983
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.
STL namespace.
int head
int tail
std::optional< int64_t > end
#define DEFINE_STRONG_INDEX_TYPE(index_type_name)