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