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