Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
circuit.cc
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#include "ortools/sat/circuit.h"
15
16#include <functional>
17#include <utility>
18#include <vector>
19
20#include "absl/container/flat_hash_map.h"
21#include "absl/container/flat_hash_set.h"
22#include "absl/log/check.h"
23#include "absl/meta/type_traits.h"
26#include "ortools/sat/integer.h"
27#include "ortools/sat/model.h"
31
32namespace operations_research {
33namespace sat {
34
36 const std::vector<int>& tails,
37 const std::vector<int>& heads,
38 const std::vector<Literal>& literals,
39 Options options, Model* model)
40 : num_nodes_(num_nodes),
41 options_(options),
42 trail_(model->GetOrCreate<Trail>()),
43 assignment_(trail_->Assignment()) {
44 CHECK(!tails.empty()) << "Empty constraint, shouldn't be constructed!";
45 next_.resize(num_nodes_, -1);
46 prev_.resize(num_nodes_, -1);
47 next_literal_.resize(num_nodes_);
48 must_be_in_cycle_.resize(num_nodes_);
49 absl::flat_hash_map<LiteralIndex, int> literal_to_watch_index;
50
51 // Temporary data to fill watch_index_to_arcs_.
52 const int num_arcs = tails.size();
53 std::vector<int> keys;
54 std::vector<Arc> values;
55 keys.reserve(num_arcs);
56 values.reserve(num_arcs);
57
58 graph_.reserve(num_arcs);
59 self_arcs_.resize(num_nodes_, kFalseLiteralIndex);
60 for (int arc = 0; arc < num_arcs; ++arc) {
61 const int head = heads[arc];
62 const int tail = tails[arc];
63 const Literal literal = literals[arc];
64 if (assignment_.LiteralIsFalse(literal)) continue;
65
66 if (tail == head) {
67 self_arcs_[tail] = literal.Index();
68 } else {
69 graph_[{tail, head}] = literal;
70 }
71
72 if (assignment_.LiteralIsTrue(literal)) {
73 if (next_[tail] != -1 || prev_[head] != -1) {
74 VLOG(1) << "Trivially UNSAT or duplicate arcs while adding " << tail
75 << " -> " << head;
76 model->GetOrCreate<SatSolver>()->NotifyThatModelIsUnsat();
77 return;
78 }
79 AddArc(tail, head, kNoLiteralIndex);
80 continue;
81 }
82
83 // Tricky: For self-arc, we watch instead when the arc become false.
84 const Literal watched_literal = tail == head ? literal.Negated() : literal;
85 const auto& it = literal_to_watch_index.find(watched_literal.Index());
86 int watch_index = it != literal_to_watch_index.end() ? it->second : -1;
87 if (watch_index == -1) {
88 watch_index = watch_index_to_literal_.size();
89 literal_to_watch_index[watched_literal.Index()] = watch_index;
90 watch_index_to_literal_.push_back(watched_literal);
91 }
92
93 keys.push_back(watch_index);
94 values.push_back({tail, head});
95 }
96 watch_index_to_arcs_.ResetFromFlatMapping(keys, values);
97
98 for (int node = 0; node < num_nodes_; ++node) {
99 if (self_arcs_[node] == kFalseLiteralIndex ||
100 assignment_.LiteralIsFalse(Literal(self_arcs_[node]))) {
101 // For the multiple_subcircuit_through_zero case, must_be_in_cycle_ will
102 // be const and only contains zero.
103 if (node == 0 || !options_.multiple_subcircuit_through_zero) {
104 must_be_in_cycle_[rev_must_be_in_cycle_size_++] = node;
105 }
106 }
107 }
108}
109
111 const int id = watcher->Register(this);
112 for (int w = 0; w < watch_index_to_literal_.size(); ++w) {
113 watcher->WatchLiteral(watch_index_to_literal_[w], id, w);
114 }
115 watcher->RegisterReversibleClass(id, this);
116 watcher->RegisterReversibleInt(id, &rev_must_be_in_cycle_size_);
117
118 // This is needed in case a Literal is used for more than one arc, we may
119 // propagate it to false/true here, and it might trigger more propagation.
120 //
121 // TODO(user): come up with a test that fail when this is not here.
123}
124
126 if (level == level_ends_.size()) return;
127 if (level > level_ends_.size()) {
128 while (level > level_ends_.size()) {
129 level_ends_.push_back(added_arcs_.size());
130 }
131 return;
132 }
133
134 // Backtrack.
135 for (int i = level_ends_[level]; i < added_arcs_.size(); ++i) {
136 const Arc arc = added_arcs_[i];
137 next_[arc.tail] = -1;
138 prev_[arc.head] = -1;
139 }
140 added_arcs_.resize(level_ends_[level]);
141 level_ends_.resize(level);
142}
143
144void CircuitPropagator::FillReasonForPath(int start_node,
145 std::vector<Literal>* reason) const {
146 CHECK_NE(start_node, -1);
147 reason->clear();
148 int node = start_node;
149 while (next_[node] != -1) {
150 if (next_literal_[node] != kNoLiteralIndex) {
151 reason->push_back(Literal(next_literal_[node]).Negated());
152 }
153 node = next_[node];
154 if (node == start_node) break;
155 }
156}
157
158// If multiple_subcircuit_through_zero is true, we never fill next_[0] and
159// prev_[0].
160void CircuitPropagator::AddArc(int tail, int head, LiteralIndex literal_index) {
161 if (tail != 0 || !options_.multiple_subcircuit_through_zero) {
162 next_[tail] = head;
163 next_literal_[tail] = literal_index;
164 }
165 if (head != 0 || !options_.multiple_subcircuit_through_zero) {
166 prev_[head] = tail;
167 }
168}
169
171 const std::vector<int>& watch_indices) {
172 for (const int w : watch_indices) {
173 const Literal literal = watch_index_to_literal_[w];
174 for (const Arc arc : watch_index_to_arcs_[w]) {
175 // Special case for self-arc.
176 if (arc.tail == arc.head) {
177 must_be_in_cycle_[rev_must_be_in_cycle_size_++] = arc.tail;
178 continue;
179 }
180
181 // Get rid of the trivial conflicts: At most one incoming and one outgoing
182 // arc for each nodes.
183 if (next_[arc.tail] != -1) {
184 std::vector<Literal>* conflict = trail_->MutableConflict();
185 if (next_literal_[arc.tail] != kNoLiteralIndex) {
186 *conflict = {Literal(next_literal_[arc.tail]).Negated(),
187 literal.Negated()};
188 } else {
189 *conflict = {literal.Negated()};
190 }
191 return false;
192 }
193 if (prev_[arc.head] != -1) {
194 std::vector<Literal>* conflict = trail_->MutableConflict();
195 if (next_literal_[prev_[arc.head]] != kNoLiteralIndex) {
196 *conflict = {Literal(next_literal_[prev_[arc.head]]).Negated(),
197 literal.Negated()};
198 } else {
199 *conflict = {literal.Negated()};
200 }
201 return false;
202 }
203
204 // Add the arc.
205 AddArc(arc.tail, arc.head, literal.Index());
206 added_arcs_.push_back(arc);
207 }
208 }
209 return Propagate();
210}
211
212// This function assumes that next_, prev_, next_literal_ and must_be_in_cycle_
213// are all up to date.
215 processed_.assign(num_nodes_, false);
216 for (int n = 0; n < num_nodes_; ++n) {
217 if (processed_[n]) continue;
218 if (next_[n] == n) continue;
219 if (next_[n] == -1 && prev_[n] == -1) continue;
220
221 // TODO(user): both this and the loop on must_be_in_cycle_ might take some
222 // time on large graph. Optimize if this become an issue.
223 in_current_path_.assign(num_nodes_, false);
224
225 // Find the start and end of the path containing node n. If this is a
226 // circuit, we will have start_node == end_node.
227 int start_node = n;
228 int end_node = n;
229 in_current_path_[n] = true;
230 processed_[n] = true;
231 while (next_[end_node] != -1) {
232 end_node = next_[end_node];
233 in_current_path_[end_node] = true;
234 processed_[end_node] = true;
235 if (end_node == n) break;
236 }
237 while (prev_[start_node] != -1) {
238 start_node = prev_[start_node];
239 in_current_path_[start_node] = true;
240 processed_[start_node] = true;
241 if (start_node == n) break;
242 }
243
244 // TODO(user): we can fail early in more case, like no more possible path
245 // to any of the mandatory node.
247 // Any cycle must contain zero.
248 if (start_node == end_node && !in_current_path_[0]) {
249 FillReasonForPath(start_node, trail_->MutableConflict());
250 return false;
251 }
252
253 // An incomplete path cannot be closed except if one of the end-points
254 // is zero.
255 if (start_node != end_node && start_node != 0 && end_node != 0) {
256 const auto it = graph_.find({end_node, start_node});
257 if (it == graph_.end()) continue;
258 const Literal literal = it->second;
259 if (assignment_.LiteralIsFalse(literal)) continue;
260
261 std::vector<Literal>* reason = trail_->GetEmptyVectorToStoreReason();
262 FillReasonForPath(start_node, reason);
263 if (!trail_->EnqueueWithStoredReason(literal.Negated())) {
264 return false;
265 }
266 }
267
268 // None of the other propagation below are valid in case of multiple
269 // circuits.
270 continue;
271 }
272
273 // Check if we miss any node that must be in the circuit. Note that the ones
274 // for which self_arcs_[i] is kFalseLiteralIndex are first. This is good as
275 // it will produce shorter reason. Otherwise we prefer the first that was
276 // assigned in the trail.
277 bool miss_some_nodes = false;
278 LiteralIndex extra_reason = kFalseLiteralIndex;
279 for (int i = 0; i < rev_must_be_in_cycle_size_; ++i) {
280 const int node = must_be_in_cycle_[i];
281 if (!in_current_path_[node]) {
282 miss_some_nodes = true;
283 extra_reason = self_arcs_[node];
284 break;
285 }
286 }
287
288 if (miss_some_nodes) {
289 // A circuit that miss a mandatory node is a conflict.
290 if (start_node == end_node) {
291 FillReasonForPath(start_node, trail_->MutableConflict());
292 if (extra_reason != kFalseLiteralIndex) {
293 trail_->MutableConflict()->push_back(Literal(extra_reason));
294 }
295 return false;
296 }
297
298 // We have an unclosed path. Propagate the fact that it cannot
299 // be closed into a cycle, i.e. not(end_node -> start_node).
300 if (start_node != end_node) {
301 const auto it = graph_.find({end_node, start_node});
302 if (it == graph_.end()) continue;
303 const Literal literal = it->second;
304 if (assignment_.LiteralIsFalse(literal)) continue;
305
306 std::vector<Literal>* reason = trail_->GetEmptyVectorToStoreReason();
307 FillReasonForPath(start_node, reason);
308 if (extra_reason != kFalseLiteralIndex) {
309 reason->push_back(Literal(extra_reason));
310 }
311 const bool ok = trail_->EnqueueWithStoredReason(literal.Negated());
312 if (!ok) return false;
313 continue;
314 }
315 }
316
317 // If we have a cycle, we can propagate all the other nodes to point to
318 // themselves. Otherwise there is nothing else to do.
319 if (start_node != end_node) continue;
320 BooleanVariable variable_with_same_reason = kNoBooleanVariable;
321 for (int node = 0; node < num_nodes_; ++node) {
322 if (in_current_path_[node]) continue;
323 if (self_arcs_[node] >= 0 &&
324 assignment_.LiteralIsTrue(Literal(self_arcs_[node]))) {
325 continue;
326 }
327
328 // This shouldn't happen because ExactlyOnePerRowAndPerColumn() should
329 // have executed first and propagated self_arcs_[node] to false.
330 CHECK_EQ(next_[node], -1);
331
332 // We should have detected that above (miss_some_nodes == true). But we
333 // still need this for corner cases where the same literal is used for
334 // many arcs, and we just propagated it here.
335 if (self_arcs_[node] == kFalseLiteralIndex ||
336 assignment_.LiteralIsFalse(Literal(self_arcs_[node]))) {
337 FillReasonForPath(start_node, trail_->MutableConflict());
338 if (self_arcs_[node] != kFalseLiteralIndex) {
339 trail_->MutableConflict()->push_back(Literal(self_arcs_[node]));
340 }
341 return false;
342 }
343
344 // Propagate.
345 const Literal literal(self_arcs_[node]);
346 if (variable_with_same_reason == kNoBooleanVariable) {
347 variable_with_same_reason = literal.Variable();
348 FillReasonForPath(start_node, trail_->GetEmptyVectorToStoreReason());
349 const bool ok = trail_->EnqueueWithStoredReason(literal);
350 if (!ok) return false;
351 } else {
352 trail_->EnqueueWithSameReasonAs(literal, variable_with_same_reason);
353 }
354 }
355 }
356 return true;
357}
358
360 const std::vector<int>& tails,
361 const std::vector<int>& heads,
362 const std::vector<Literal>& literals,
363 Model* model)
364 : num_nodes_(num_nodes),
365 trail_(model->GetOrCreate<Trail>()),
366 assignment_(trail_->Assignment()) {
367 CHECK(!tails.empty()) << "Empty constraint, shouldn't be constructed!";
368
369 graph_.resize(num_nodes);
370 graph_literals_.resize(num_nodes);
371
372 const int num_arcs = tails.size();
373 absl::flat_hash_map<LiteralIndex, int> literal_to_watch_index;
374 for (int arc = 0; arc < num_arcs; ++arc) {
375 const int head = heads[arc];
376 const int tail = tails[arc];
377 const Literal literal = literals[arc];
378
379 if (assignment_.LiteralIsFalse(literal)) continue;
380 if (assignment_.LiteralIsTrue(literal)) {
381 // Fixed arc. It will never be removed.
382 graph_[tail].push_back(head);
383 graph_literals_[tail].push_back(literal);
384 continue;
385 }
386
387 // We have to deal with the same literal controlling more than one arc.
388 const auto [it, inserted] = literal_to_watch_index.insert(
389 {literal.Index(), watch_index_to_literal_.size()});
390 if (inserted) {
391 watch_index_to_literal_.push_back(literal);
392 watch_index_to_arcs_.push_back({});
393 }
394 watch_index_to_arcs_[it->second].push_back({tail, head});
395 }
396
397 // We register at construction.
398 //
399 // TODO(user): Uniformize this across propagator. Sometimes it is nice not
400 // to register them, but most of them can be registered right away.
401 RegisterWith(model->GetOrCreate<GenericLiteralWatcher>());
402}
403
404void NoCyclePropagator::RegisterWith(GenericLiteralWatcher* watcher) {
405 const int id = watcher->Register(this);
406 for (int w = 0; w < watch_index_to_literal_.size(); ++w) {
407 watcher->WatchLiteral(watch_index_to_literal_[w], id, w);
408 }
409 watcher->RegisterReversibleClass(id, this);
410
411 // This class currently only test for conflict, so no need to call it twice.
412 // watcher->NotifyThatPropagatorMayNotReachFixedPointInOnePass(id);
413}
414
416 if (level == level_ends_.size()) return;
417 if (level > level_ends_.size()) {
418 while (level > level_ends_.size()) {
419 level_ends_.push_back(touched_nodes_.size());
420 }
421 return;
422 }
423
424 // Backtrack.
425 for (int i = level_ends_[level]; i < touched_nodes_.size(); ++i) {
426 graph_literals_[touched_nodes_[i]].pop_back();
427 graph_[touched_nodes_[i]].pop_back();
428 }
429 touched_nodes_.resize(level_ends_[level]);
430 level_ends_.resize(level);
431}
432
434 const std::vector<int>& watch_indices) {
435 for (const int w : watch_indices) {
436 const Literal literal = watch_index_to_literal_[w];
437 for (const auto& [tail, head] : watch_index_to_arcs_[w]) {
438 graph_[tail].push_back(head);
439 graph_literals_[tail].push_back(literal);
440 touched_nodes_.push_back(tail);
441 }
442 }
443 return Propagate();
444}
445
446// TODO(user): only explore node with newly added arcs.
447//
448// TODO(user): We could easily re-index the graph so that only nodes with arcs
449// are used. Because right now we are in O(num_nodes) even if the graph is
450// empty.
452 // The graph should be up to date when this is called thanks to
453 // IncrementalPropagate(). We just do a SCC on the graph.
454 components_.clear();
455 FindStronglyConnectedComponents(num_nodes_, graph_, &components_);
456
457 for (const std::vector<int>& compo : components_) {
458 if (compo.size() <= 1) continue;
459
460 // We collect all arc from this compo.
461 //
462 // TODO(user): We could be more efficient here, but this is only executed on
463 // conflicts. We should at least make sure we return a single cycle even
464 // though if this is called often enough, we shouldn't have a lot more than
465 // this.
466 absl::flat_hash_set<int> nodes(compo.begin(), compo.end());
467 std::vector<Literal>* conflict = trail_->MutableConflict();
468 conflict->clear();
469 for (const int tail : compo) {
470 const int degree = graph_[tail].size();
471 CHECK_EQ(degree, graph_literals_[tail].size());
472 for (int i = 0; i < degree; ++i) {
473 if (nodes.contains(graph_[tail][i])) {
474 conflict->push_back(graph_literals_[tail][i].Negated());
475 }
476 }
477 }
478 return false;
479 }
480
481 return true;
482}
483
485 std::vector<std::vector<Literal>> graph,
486 const std::vector<int>& distinguished_nodes, Model* model)
487 : graph_(std::move(graph)),
488 num_nodes_(graph_.size()),
489 trail_(model->GetOrCreate<Trail>()) {
490 node_is_distinguished_.resize(num_nodes_, false);
491 for (const int node : distinguished_nodes) {
492 node_is_distinguished_[node] = true;
493 }
494}
495
497 const int watcher_id = watcher->Register(this);
498
499 // Fill fixed_arcs_ with arcs that are initially fixed to true,
500 // assign arcs to watch indices.
501 for (int node1 = 0; node1 < num_nodes_; node1++) {
502 for (int node2 = 0; node2 < num_nodes_; node2++) {
503 const Literal l = graph_[node1][node2];
504 if (trail_->Assignment().LiteralIsFalse(l)) continue;
505 if (trail_->Assignment().LiteralIsTrue(l)) {
506 fixed_arcs_.emplace_back(node1, node2);
507 } else {
508 watcher->WatchLiteral(l, watcher_id, watch_index_to_arc_.size());
509 watch_index_to_arc_.emplace_back(node1, node2);
510 }
511 }
512 }
513 watcher->RegisterReversibleClass(watcher_id, this);
514}
515
517 if (level == level_ends_.size()) return;
518 if (level > level_ends_.size()) {
519 while (level > level_ends_.size()) {
520 level_ends_.push_back(fixed_arcs_.size());
521 }
522 } else {
523 // Backtrack.
524 fixed_arcs_.resize(level_ends_[level]);
525 level_ends_.resize(level);
526 }
527}
528
530 const std::vector<int>& watch_indices) {
531 for (const int w : watch_indices) {
532 const auto& arc = watch_index_to_arc_[w];
533 fixed_arcs_.push_back(arc);
534 }
535 return Propagate();
536}
537
538void CircuitCoveringPropagator::FillFixedPathInReason(
539 int start, int end, std::vector<Literal>* reason) {
540 reason->clear();
541 int current = start;
542 do {
543 DCHECK_NE(next_[current], -1);
544 DCHECK(trail_->Assignment().LiteralIsTrue(graph_[current][next_[current]]));
545 reason->push_back(graph_[current][next_[current]].Negated());
546 current = next_[current];
547 } while (current != end);
548}
549
551 // Gather next_ and prev_ from fixed arcs.
552 next_.assign(num_nodes_, -1);
553 prev_.assign(num_nodes_, -1);
554 for (const auto& arc : fixed_arcs_) {
555 // Two arcs go out of arc.first, forbidden.
556 if (next_[arc.first] != -1) {
557 *trail_->MutableConflict() = {
558 graph_[arc.first][next_[arc.first]].Negated(),
559 graph_[arc.first][arc.second].Negated()};
560 return false;
561 }
562 next_[arc.first] = arc.second;
563 // Two arcs come into arc.second, forbidden.
564 if (prev_[arc.second] != -1) {
565 *trail_->MutableConflict() = {
566 graph_[prev_[arc.second]][arc.second].Negated(),
567 graph_[arc.first][arc.second].Negated()};
568 return false;
569 }
570 prev_[arc.second] = arc.first;
571 }
572
573 // For every node, find partial path/circuit in which the node is.
574 // Use visited_ to visit each path/circuit only once.
575 visited_.assign(num_nodes_, false);
576 for (int node = 0; node < num_nodes_; node++) {
577 // Skip if already visited, isolated or loop.
578 if (visited_[node]) continue;
579 if (prev_[node] == -1 && next_[node] == -1) continue;
580 if (prev_[node] == node) continue;
581
582 // Find start of path/circuit.
583 int start = node;
584 for (int current = prev_[node]; current != -1 && current != node;
585 current = prev_[current]) {
586 start = current;
587 }
588
589 // Find distinguished node of path. Fail if there are several,
590 // fail if this is a non loop circuit and there are none.
591 int distinguished = node_is_distinguished_[start] ? start : -1;
592 int current = next_[start];
593 int end = start;
594 visited_[start] = true;
595 while (current != -1 && current != start) {
596 if (node_is_distinguished_[current]) {
597 if (distinguished != -1) {
598 FillFixedPathInReason(distinguished, current,
599 trail_->MutableConflict());
600 return false;
601 }
602 distinguished = current;
603 }
604 visited_[current] = true;
605 end = current;
606 current = next_[current];
607 }
608
609 // Circuit with no distinguished nodes, forbidden.
610 if (start == current && distinguished == -1) {
611 FillFixedPathInReason(start, start, trail_->MutableConflict());
612 return false;
613 }
614
615 // Path with no distinguished node: forbid to close it.
616 if (current == -1 && distinguished == -1 &&
617 !trail_->Assignment().LiteralIsFalse(graph_[end][start])) {
618 auto* reason = trail_->GetEmptyVectorToStoreReason();
619 FillFixedPathInReason(start, end, reason);
620 const bool ok =
621 trail_->EnqueueWithStoredReason(graph_[end][start].Negated());
622 if (!ok) return false;
623 }
624 }
625 return true;
626}
627
629 const std::vector<std::vector<Literal>>& graph) {
630 return [=](Model* model) {
631 const int n = graph.size();
632 std::vector<Literal> exactly_one_constraint;
633 exactly_one_constraint.reserve(n);
634 for (const bool transpose : {false, true}) {
635 for (int i = 0; i < n; ++i) {
636 exactly_one_constraint.clear();
637 for (int j = 0; j < n; ++j) {
638 exactly_one_constraint.push_back(transpose ? graph[j][i]
639 : graph[i][j]);
640 }
641 model->Add(ExactlyOneConstraint(exactly_one_constraint));
642 }
643 }
644 };
645}
646
647void LoadSubcircuitConstraint(int num_nodes, const std::vector<int>& tails,
648 const std::vector<int>& heads,
649 const std::vector<Literal>& literals,
650 Model* model,
651 bool multiple_subcircuit_through_zero) {
652 const int num_arcs = tails.size();
653 CHECK_GT(num_arcs, 0);
654 CHECK_EQ(heads.size(), num_arcs);
655 CHECK_EQ(literals.size(), num_arcs);
656
657 // If a node has no outgoing or no incoming arc, the model will be unsat
658 // as soon as we add the corresponding ExactlyOneConstraint().
659 auto sat_solver = model->GetOrCreate<SatSolver>();
660 auto implications = model->GetOrCreate<BinaryImplicationGraph>();
661
662 std::vector<std::vector<Literal>> exactly_one_incoming(num_nodes);
663 std::vector<std::vector<Literal>> exactly_one_outgoing(num_nodes);
664 for (int arc = 0; arc < num_arcs; arc++) {
665 const int tail = tails[arc];
666 const int head = heads[arc];
667 exactly_one_outgoing[tail].push_back(literals[arc]);
668 exactly_one_incoming[head].push_back(literals[arc]);
669 }
670 for (int i = 0; i < exactly_one_incoming.size(); ++i) {
671 if (i == 0 && multiple_subcircuit_through_zero) continue;
672 if (!implications->AddAtMostOne(exactly_one_incoming[i])) {
673 sat_solver->NotifyThatModelIsUnsat();
674 return;
675 }
676 sat_solver->AddProblemClause(exactly_one_incoming[i]);
677 if (sat_solver->ModelIsUnsat()) return;
678 }
679 for (int i = 0; i < exactly_one_outgoing.size(); ++i) {
680 if (i == 0 && multiple_subcircuit_through_zero) continue;
681 if (!implications->AddAtMostOne(exactly_one_outgoing[i])) {
682 sat_solver->NotifyThatModelIsUnsat();
683 return;
684 }
685 sat_solver->AddProblemClause(exactly_one_outgoing[i]);
686 if (sat_solver->ModelIsUnsat()) return;
687 }
688
690 options.multiple_subcircuit_through_zero = multiple_subcircuit_through_zero;
691 CircuitPropagator* constraint =
692 new CircuitPropagator(num_nodes, tails, heads, literals, options, model);
693 constraint->RegisterWith(model->GetOrCreate<GenericLiteralWatcher>());
694 model->TakeOwnership(constraint);
695}
696
697std::function<void(Model*)> CircuitCovering(
698 const std::vector<std::vector<Literal>>& graph,
699 const std::vector<int>& distinguished_nodes) {
700 return [=](Model* model) {
701 CircuitCoveringPropagator* constraint =
702 new CircuitCoveringPropagator(graph, distinguished_nodes, model);
703 constraint->RegisterWith(model->GetOrCreate<GenericLiteralWatcher>());
704 model->TakeOwnership(constraint);
705 };
706}
707
708} // namespace sat
709} // namespace operations_research
IntegerValue size
CircuitCoveringPropagator(std::vector< std::vector< Literal > > graph, const std::vector< int > &distinguished_nodes, Model *model)
Definition circuit.cc:484
void RegisterWith(GenericLiteralWatcher *watcher)
Definition circuit.cc:496
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
Definition circuit.cc:529
void RegisterWith(GenericLiteralWatcher *watcher)
Definition circuit.cc:110
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
Definition circuit.cc:170
CircuitPropagator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Options options, Model *model)
Definition circuit.cc:35
void ResetFromFlatMapping(Keys keys, Values values)
Definition util.h:830
void WatchLiteral(Literal l, int id, int watch_index=-1)
Definition integer.h:1844
void RegisterReversibleClass(int id, ReversibleInterface *rev)
Definition integer.cc:2374
int Register(PropagatorInterface *propagator)
Registers a propagator and returns its unique ids.
Definition integer.cc:2332
LiteralIndex Index() const
Definition sat_base.h:91
NoCyclePropagator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Model *model)
Definition circuit.cc:359
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
Definition circuit.cc:433
std::vector< Literal > * MutableConflict()
Definition sat_base.h:433
void EnqueueWithSameReasonAs(Literal true_literal, BooleanVariable reference_var)
Definition sat_base.h:335
std::vector< Literal > * GetEmptyVectorToStoreReason(int trail_index) const
Definition sat_base.h:393
ABSL_MUST_USE_RESULT bool EnqueueWithStoredReason(Literal true_literal)
Definition sat_base.h:347
const VariablesAssignment & Assignment() const
Definition sat_base.h:462
bool LiteralIsFalse(Literal literal) const
Definition sat_base.h:185
bool LiteralIsTrue(Literal literal) const
Definition sat_base.h:188
GraphType graph
GRBmodel * model
int literal
int arc
const LiteralIndex kNoLiteralIndex(-1)
std::function< void(Model *)> CircuitCovering(const std::vector< std::vector< Literal > > &graph, const std::vector< int > &distinguished_nodes)
Definition circuit.cc:697
void LoadSubcircuitConstraint(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Model *model, bool multiple_subcircuit_through_zero)
Definition circuit.cc:647
const LiteralIndex kFalseLiteralIndex(-3)
std::function< void(Model *)> ExactlyOnePerRowAndPerColumn(const std::vector< std::vector< Literal > > &graph)
Definition circuit.cc:628
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
Definition sat_solver.h:905
const BooleanVariable kNoBooleanVariable(-1)
In SWIG mode, we don't want anything besides these top-level includes.
STL namespace.
trees with all degrees equal w the current value of w
int head
int tail
int nodes
std::optional< int64_t > end
int64_t start
void FindStronglyConnectedComponents(NodeIndex num_nodes, const Graph &graph, SccOutput *components)
Simple wrapper function for most usage.