OR-Tools  9.3
circuit.cc
Go to the documentation of this file.
1// Copyright 2010-2021 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/meta/type_traits.h"
24#include "ortools/sat/integer.h"
25#include "ortools/sat/model.h"
29
30namespace operations_research {
31namespace sat {
32
34 const std::vector<int>& tails,
35 const std::vector<int>& heads,
36 const std::vector<Literal>& literals,
37 Options options, Model* model)
38 : num_nodes_(num_nodes),
39 options_(options),
40 trail_(model->GetOrCreate<Trail>()),
41 assignment_(trail_->Assignment()) {
42 CHECK(!tails.empty()) << "Empty constraint, shouldn't be constructed!";
43 next_.resize(num_nodes_, -1);
44 prev_.resize(num_nodes_, -1);
45 next_literal_.resize(num_nodes_);
46 must_be_in_cycle_.resize(num_nodes_);
47 absl::flat_hash_map<LiteralIndex, int> literal_to_watch_index;
48
49 const int num_arcs = tails.size();
50 graph_.reserve(num_arcs);
51 self_arcs_.resize(num_nodes_,
52 model->GetOrCreate<IntegerEncoder>()->GetFalseLiteral());
53 for (int arc = 0; arc < num_arcs; ++arc) {
54 const int head = heads[arc];
55 const int tail = tails[arc];
56 const Literal literal = literals[arc];
57 if (assignment_.LiteralIsFalse(literal)) continue;
58
59 if (tail == head) {
60 self_arcs_[tail] = literal;
61 } else {
62 graph_[{tail, head}] = literal;
63 }
64
65 if (assignment_.LiteralIsTrue(literal)) {
66 if (next_[tail] != -1 || prev_[head] != -1) {
67 VLOG(1) << "Trivially UNSAT or duplicate arcs while adding " << tail
68 << " -> " << head;
69 model->GetOrCreate<SatSolver>()->NotifyThatModelIsUnsat();
70 return;
71 }
72 AddArc(tail, head, kNoLiteralIndex);
73 continue;
74 }
75
76 // Tricky: For self-arc, we watch instead when the arc become false.
77 const Literal watched_literal = tail == head ? literal.Negated() : literal;
78 int watch_index = gtl::FindWithDefault(literal_to_watch_index,
79 watched_literal.Index(), -1);
80 if (watch_index == -1) {
81 watch_index = watch_index_to_literal_.size();
82 literal_to_watch_index[watched_literal.Index()] = watch_index;
83 watch_index_to_literal_.push_back(watched_literal);
84 watch_index_to_arcs_.push_back(std::vector<Arc>());
85 }
86 watch_index_to_arcs_[watch_index].push_back({tail, head});
87 }
88
89 for (int node = 0; node < num_nodes_; ++node) {
90 if (assignment_.LiteralIsFalse(self_arcs_[node])) {
91 // For the multiple_subcircuit_through_zero case, must_be_in_cycle_ will
92 // be const and only contains zero.
93 if (node == 0 || !options_.multiple_subcircuit_through_zero) {
94 must_be_in_cycle_[rev_must_be_in_cycle_size_++] = node;
95 }
96 }
97 }
98}
99
101 const int id = watcher->Register(this);
102 for (int w = 0; w < watch_index_to_literal_.size(); ++w) {
103 watcher->WatchLiteral(watch_index_to_literal_[w], id, w);
104 }
105 watcher->RegisterReversibleClass(id, this);
106 watcher->RegisterReversibleInt(id, &propagation_trail_index_);
107 watcher->RegisterReversibleInt(id, &rev_must_be_in_cycle_size_);
108
109 // This is needed in case a Literal is used for more than one arc, we may
110 // propagate it to false/true here, and it might trigger more propagation.
111 //
112 // TODO(user): come up with a test that fail when this is not here.
114}
115
117 if (level == level_ends_.size()) return;
118 if (level > level_ends_.size()) {
119 while (level > level_ends_.size()) {
120 level_ends_.push_back(added_arcs_.size());
121 }
122 return;
123 }
124
125 // Backtrack.
126 for (int i = level_ends_[level]; i < added_arcs_.size(); ++i) {
127 const Arc arc = added_arcs_[i];
128 next_[arc.tail] = -1;
129 prev_[arc.head] = -1;
130 }
131 added_arcs_.resize(level_ends_[level]);
132 level_ends_.resize(level);
133}
134
135void CircuitPropagator::FillReasonForPath(int start_node,
136 std::vector<Literal>* reason) const {
137 CHECK_NE(start_node, -1);
138 reason->clear();
139 int node = start_node;
140 while (next_[node] != -1) {
141 if (next_literal_[node] != kNoLiteralIndex) {
142 reason->push_back(Literal(next_literal_[node]).Negated());
143 }
144 node = next_[node];
145 if (node == start_node) break;
146 }
147}
148
149// If multiple_subcircuit_through_zero is true, we never fill next_[0] and
150// prev_[0].
151void CircuitPropagator::AddArc(int tail, int head, LiteralIndex literal_index) {
152 if (tail != 0 || !options_.multiple_subcircuit_through_zero) {
153 next_[tail] = head;
154 next_literal_[tail] = literal_index;
155 }
156 if (head != 0 || !options_.multiple_subcircuit_through_zero) {
157 prev_[head] = tail;
158 }
159}
160
162 const std::vector<int>& watch_indices) {
163 for (const int w : watch_indices) {
164 const Literal literal = watch_index_to_literal_[w];
165 for (const Arc arc : watch_index_to_arcs_[w]) {
166 // Special case for self-arc.
167 if (arc.tail == arc.head) {
168 must_be_in_cycle_[rev_must_be_in_cycle_size_++] = arc.tail;
169 continue;
170 }
171
172 // Get rid of the trivial conflicts: At most one incoming and one outgoing
173 // arc for each nodes.
174 if (next_[arc.tail] != -1) {
175 std::vector<Literal>* conflict = trail_->MutableConflict();
176 if (next_literal_[arc.tail] != kNoLiteralIndex) {
177 *conflict = {Literal(next_literal_[arc.tail]).Negated(),
178 literal.Negated()};
179 } else {
180 *conflict = {literal.Negated()};
181 }
182 return false;
183 }
184 if (prev_[arc.head] != -1) {
185 std::vector<Literal>* conflict = trail_->MutableConflict();
186 if (next_literal_[prev_[arc.head]] != kNoLiteralIndex) {
187 *conflict = {Literal(next_literal_[prev_[arc.head]]).Negated(),
188 literal.Negated()};
189 } else {
190 *conflict = {literal.Negated()};
191 }
192 return false;
193 }
194
195 // Add the arc.
196 AddArc(arc.tail, arc.head, literal.Index());
197 added_arcs_.push_back(arc);
198 }
199 }
200 return Propagate();
201}
202
203// This function assumes that next_, prev_, next_literal_ and must_be_in_cycle_
204// are all up to date.
206 processed_.assign(num_nodes_, false);
207 for (int n = 0; n < num_nodes_; ++n) {
208 if (processed_[n]) continue;
209 if (next_[n] == n) continue;
210 if (next_[n] == -1 && prev_[n] == -1) continue;
211
212 // TODO(user): both this and the loop on must_be_in_cycle_ might take some
213 // time on large graph. Optimize if this become an issue.
214 in_current_path_.assign(num_nodes_, false);
215
216 // Find the start and end of the path containing node n. If this is a
217 // circuit, we will have start_node == end_node.
218 int start_node = n;
219 int end_node = n;
220 in_current_path_[n] = true;
221 processed_[n] = true;
222 while (next_[end_node] != -1) {
223 end_node = next_[end_node];
224 in_current_path_[end_node] = true;
225 processed_[end_node] = true;
226 if (end_node == n) break;
227 }
228 while (prev_[start_node] != -1) {
229 start_node = prev_[start_node];
230 in_current_path_[start_node] = true;
231 processed_[start_node] = true;
232 if (start_node == n) break;
233 }
234
235 // TODO(user): we can fail early in more case, like no more possible path
236 // to any of the mandatory node.
238 // Any cycle must contain zero.
239 if (start_node == end_node && !in_current_path_[0]) {
240 FillReasonForPath(start_node, trail_->MutableConflict());
241 return false;
242 }
243
244 // An incomplete path cannot be closed except if one of the end-points
245 // is zero.
246 if (start_node != end_node && start_node != 0 && end_node != 0) {
247 const auto it = graph_.find({end_node, start_node});
248 if (it == graph_.end()) continue;
249 const Literal literal = it->second;
250 if (assignment_.LiteralIsFalse(literal)) continue;
251
252 std::vector<Literal>* reason = trail_->GetEmptyVectorToStoreReason();
253 FillReasonForPath(start_node, reason);
254 if (!trail_->EnqueueWithStoredReason(literal.Negated())) {
255 return false;
256 }
257 }
258
259 // None of the other propagation below are valid in case of multiple
260 // circuits.
261 continue;
262 }
263
264 // Check if we miss any node that must be in the circuit. Note that the ones
265 // for which self_arcs_[i] is kFalseLiteralIndex are first. This is good as
266 // it will produce shorter reason. Otherwise we prefer the first that was
267 // assigned in the trail.
268 bool miss_some_nodes = false;
269 LiteralIndex extra_reason = kFalseLiteralIndex;
270 for (int i = 0; i < rev_must_be_in_cycle_size_; ++i) {
271 const int node = must_be_in_cycle_[i];
272 if (!in_current_path_[node]) {
273 miss_some_nodes = true;
274 extra_reason = self_arcs_[node].Index();
275 break;
276 }
277 }
278
279 if (miss_some_nodes) {
280 // A circuit that miss a mandatory node is a conflict.
281 if (start_node == end_node) {
282 FillReasonForPath(start_node, trail_->MutableConflict());
283 if (extra_reason != kFalseLiteralIndex) {
284 trail_->MutableConflict()->push_back(Literal(extra_reason));
285 }
286 return false;
287 }
288
289 // We have an unclosed path. Propagate the fact that it cannot
290 // be closed into a cycle, i.e. not(end_node -> start_node).
291 if (start_node != end_node) {
292 const auto it = graph_.find({end_node, start_node});
293 if (it == graph_.end()) continue;
294 const Literal literal = it->second;
295 if (assignment_.LiteralIsFalse(literal)) continue;
296
297 std::vector<Literal>* reason = trail_->GetEmptyVectorToStoreReason();
298 FillReasonForPath(start_node, reason);
299 if (extra_reason != kFalseLiteralIndex) {
300 reason->push_back(Literal(extra_reason));
301 }
302 const bool ok = trail_->EnqueueWithStoredReason(literal.Negated());
303 if (!ok) return false;
304 continue;
305 }
306 }
307
308 // If we have a cycle, we can propagate all the other nodes to point to
309 // themselves. Otherwise there is nothing else to do.
310 if (start_node != end_node) continue;
311 BooleanVariable variable_with_same_reason = kNoBooleanVariable;
312 for (int node = 0; node < num_nodes_; ++node) {
313 if (in_current_path_[node]) continue;
314 if (assignment_.LiteralIsTrue(self_arcs_[node])) continue;
315
316 // This shouldn't happen because ExactlyOnePerRowAndPerColumn() should
317 // have executed first and propagated self_arcs_[node] to false.
318 CHECK_EQ(next_[node], -1);
319
320 // We should have detected that above (miss_some_nodes == true). But we
321 // still need this for corner cases where the same literal is used for
322 // many arcs, and we just propagated it here.
323 if (assignment_.LiteralIsFalse(self_arcs_[node])) {
324 FillReasonForPath(start_node, trail_->MutableConflict());
325 trail_->MutableConflict()->push_back(self_arcs_[node]);
326 return false;
327 }
328
329 // Propagate.
330 const Literal literal(self_arcs_[node]);
331 if (variable_with_same_reason == kNoBooleanVariable) {
332 variable_with_same_reason = literal.Variable();
333 FillReasonForPath(start_node, trail_->GetEmptyVectorToStoreReason());
334 const bool ok = trail_->EnqueueWithStoredReason(literal);
335 if (!ok) return false;
336 } else {
337 trail_->EnqueueWithSameReasonAs(literal, variable_with_same_reason);
338 }
339 }
340 }
341 return true;
342}
343
345 std::vector<std::vector<Literal>> graph,
346 const std::vector<int>& distinguished_nodes, Model* model)
347 : graph_(std::move(graph)),
348 num_nodes_(graph_.size()),
349 trail_(model->GetOrCreate<Trail>()) {
350 node_is_distinguished_.resize(num_nodes_, false);
351 for (const int node : distinguished_nodes) {
352 node_is_distinguished_[node] = true;
353 }
354}
355
357 const int watcher_id = watcher->Register(this);
358
359 // Fill fixed_arcs_ with arcs that are initially fixed to true,
360 // assign arcs to watch indices.
361 for (int node1 = 0; node1 < num_nodes_; node1++) {
362 for (int node2 = 0; node2 < num_nodes_; node2++) {
363 const Literal l = graph_[node1][node2];
364 if (trail_->Assignment().LiteralIsFalse(l)) continue;
365 if (trail_->Assignment().LiteralIsTrue(l)) {
366 fixed_arcs_.emplace_back(node1, node2);
367 } else {
368 watcher->WatchLiteral(l, watcher_id, watch_index_to_arc_.size());
369 watch_index_to_arc_.emplace_back(node1, node2);
370 }
371 }
372 }
373 watcher->RegisterReversibleClass(watcher_id, this);
374}
375
377 if (level == level_ends_.size()) return;
378 if (level > level_ends_.size()) {
379 while (level > level_ends_.size()) {
380 level_ends_.push_back(fixed_arcs_.size());
381 }
382 } else {
383 // Backtrack.
384 fixed_arcs_.resize(level_ends_[level]);
385 level_ends_.resize(level);
386 }
387}
388
390 const std::vector<int>& watch_indices) {
391 for (const int w : watch_indices) {
392 const auto& arc = watch_index_to_arc_[w];
393 fixed_arcs_.push_back(arc);
394 }
395 return Propagate();
396}
397
398void CircuitCoveringPropagator::FillFixedPathInReason(
399 int start, int end, std::vector<Literal>* reason) {
400 reason->clear();
401 int current = start;
402 do {
403 DCHECK_NE(next_[current], -1);
404 DCHECK(trail_->Assignment().LiteralIsTrue(graph_[current][next_[current]]));
405 reason->push_back(graph_[current][next_[current]].Negated());
406 current = next_[current];
407 } while (current != end);
408}
409
411 // Gather next_ and prev_ from fixed arcs.
412 next_.assign(num_nodes_, -1);
413 prev_.assign(num_nodes_, -1);
414 for (const auto& arc : fixed_arcs_) {
415 // Two arcs go out of arc.first, forbidden.
416 if (next_[arc.first] != -1) {
417 *trail_->MutableConflict() = {
418 graph_[arc.first][next_[arc.first]].Negated(),
419 graph_[arc.first][arc.second].Negated()};
420 return false;
421 }
422 next_[arc.first] = arc.second;
423 // Two arcs come into arc.second, forbidden.
424 if (prev_[arc.second] != -1) {
425 *trail_->MutableConflict() = {
426 graph_[prev_[arc.second]][arc.second].Negated(),
427 graph_[arc.first][arc.second].Negated()};
428 return false;
429 }
430 prev_[arc.second] = arc.first;
431 }
432
433 // For every node, find partial path/circuit in which the node is.
434 // Use visited_ to visit each path/circuit only once.
435 visited_.assign(num_nodes_, false);
436 for (int node = 0; node < num_nodes_; node++) {
437 // Skip if already visited, isolated or loop.
438 if (visited_[node]) continue;
439 if (prev_[node] == -1 && next_[node] == -1) continue;
440 if (prev_[node] == node) continue;
441
442 // Find start of path/circuit.
443 int start = node;
444 for (int current = prev_[node]; current != -1 && current != node;
445 current = prev_[current]) {
446 start = current;
447 }
448
449 // Find distinguished node of path. Fail if there are several,
450 // fail if this is a non loop circuit and there are none.
451 int distinguished = node_is_distinguished_[start] ? start : -1;
452 int current = next_[start];
453 int end = start;
454 visited_[start] = true;
455 while (current != -1 && current != start) {
456 if (node_is_distinguished_[current]) {
457 if (distinguished != -1) {
458 FillFixedPathInReason(distinguished, current,
459 trail_->MutableConflict());
460 return false;
461 }
462 distinguished = current;
463 }
464 visited_[current] = true;
465 end = current;
466 current = next_[current];
467 }
468
469 // Circuit with no distinguished nodes, forbidden.
470 if (start == current && distinguished == -1) {
471 FillFixedPathInReason(start, start, trail_->MutableConflict());
472 return false;
473 }
474
475 // Path with no distinguished node: forbid to close it.
476 if (current == -1 && distinguished == -1 &&
477 !trail_->Assignment().LiteralIsFalse(graph_[end][start])) {
478 auto* reason = trail_->GetEmptyVectorToStoreReason();
479 FillFixedPathInReason(start, end, reason);
480 const bool ok =
481 trail_->EnqueueWithStoredReason(graph_[end][start].Negated());
482 if (!ok) return false;
483 }
484 }
485 return true;
486}
487
489 const std::vector<std::vector<Literal>>& graph) {
490 return [=](Model* model) {
491 const int n = graph.size();
492 std::vector<Literal> exactly_one_constraint;
493 exactly_one_constraint.reserve(n);
494 for (const bool transpose : {false, true}) {
495 for (int i = 0; i < n; ++i) {
496 exactly_one_constraint.clear();
497 for (int j = 0; j < n; ++j) {
498 exactly_one_constraint.push_back(transpose ? graph[j][i]
499 : graph[i][j]);
500 }
501 model->Add(ExactlyOneConstraint(exactly_one_constraint));
502 }
503 }
504 };
505}
506
507std::function<void(Model*)> SubcircuitConstraint(
508 int num_nodes, const std::vector<int>& tails, const std::vector<int>& heads,
509 const std::vector<Literal>& literals,
510 bool multiple_subcircuit_through_zero) {
511 return [=](Model* model) {
512 const int num_arcs = tails.size();
513 CHECK_GT(num_arcs, 0);
514 CHECK_EQ(heads.size(), num_arcs);
515 CHECK_EQ(literals.size(), num_arcs);
516
517 // If a node has no outgoing or no incoming arc, the model will be unsat
518 // as soon as we add the corresponding ExactlyOneConstraint().
519 auto sat_solver = model->GetOrCreate<SatSolver>();
520
521 std::vector<std::vector<Literal>> exactly_one_incoming(num_nodes);
522 std::vector<std::vector<Literal>> exactly_one_outgoing(num_nodes);
523 for (int arc = 0; arc < num_arcs; arc++) {
524 const int tail = tails[arc];
525 const int head = heads[arc];
526 exactly_one_outgoing[tail].push_back(literals[arc]);
527 exactly_one_incoming[head].push_back(literals[arc]);
528 }
529 for (int i = 0; i < exactly_one_incoming.size(); ++i) {
530 if (i == 0 && multiple_subcircuit_through_zero) continue;
531 model->Add(ExactlyOneConstraint(exactly_one_incoming[i]));
532 if (sat_solver->IsModelUnsat()) return;
533 }
534 for (int i = 0; i < exactly_one_outgoing.size(); ++i) {
535 if (i == 0 && multiple_subcircuit_through_zero) continue;
536 model->Add(ExactlyOneConstraint(exactly_one_outgoing[i]));
537 if (sat_solver->IsModelUnsat()) return;
538 }
539
541 options.multiple_subcircuit_through_zero = multiple_subcircuit_through_zero;
542 CircuitPropagator* constraint = new CircuitPropagator(
543 num_nodes, tails, heads, literals, options, model);
544 constraint->RegisterWith(model->GetOrCreate<GenericLiteralWatcher>());
545 model->TakeOwnership(constraint);
546 };
547}
548
549std::function<void(Model*)> CircuitCovering(
550 const std::vector<std::vector<Literal>>& graph,
551 const std::vector<int>& distinguished_nodes) {
552 return [=](Model* model) {
553 CircuitCoveringPropagator* constraint =
554 new CircuitCoveringPropagator(graph, distinguished_nodes, model);
555 constraint->RegisterWith(model->GetOrCreate<GenericLiteralWatcher>());
556 model->TakeOwnership(constraint);
557 };
558}
559
560} // namespace sat
561} // namespace operations_research
#define CHECK(condition)
Definition: base/logging.h:495
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:892
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
#define CHECK_GT(val1, val2)
Definition: base/logging.h:708
#define CHECK_NE(val1, val2)
Definition: base/logging.h:704
#define DCHECK(condition)
Definition: base/logging.h:890
#define VLOG(verboselevel)
Definition: base/logging.h:984
An Assignment is a variable -> domains mapping, used to report solutions to the user.
CircuitCoveringPropagator(std::vector< std::vector< Literal > > graph, const std::vector< int > &distinguished_nodes, Model *model)
Definition: circuit.cc:344
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
Definition: circuit.cc:389
void RegisterWith(GenericLiteralWatcher *watcher)
Definition: circuit.cc:356
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
Definition: circuit.cc:161
void RegisterWith(GenericLiteralWatcher *watcher)
Definition: circuit.cc:100
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:33
void RegisterReversibleClass(int id, ReversibleInterface *rev)
Definition: integer.cc:2067
void WatchLiteral(Literal l, int id, int watch_index=-1)
Definition: integer.h:1561
int Register(PropagatorInterface *propagator)
Definition: integer.cc:2028
LiteralIndex Index() const
Definition: sat_base.h:87
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
std::vector< Literal > * GetEmptyVectorToStoreReason(int trail_index) const
Definition: sat_base.h:323
void EnqueueWithSameReasonAs(Literal true_literal, BooleanVariable reference_var)
Definition: sat_base.h:275
const VariablesAssignment & Assignment() const
Definition: sat_base.h:383
ABSL_MUST_USE_RESULT bool EnqueueWithStoredReason(Literal true_literal)
Definition: sat_base.h:287
std::vector< Literal > * MutableConflict()
Definition: sat_base.h:364
bool LiteralIsTrue(Literal literal) const
Definition: sat_base.h:153
bool LiteralIsFalse(Literal literal) const
Definition: sat_base.h:150
GRBmodel * model
int arc
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:29
const LiteralIndex kNoLiteralIndex(-1)
std::function< void(Model *)> SubcircuitConstraint(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, bool multiple_subcircuit_through_zero)
Definition: circuit.cc:507
std::function< void(Model *)> CircuitCovering(const std::vector< std::vector< Literal > > &graph, const std::vector< int > &distinguished_nodes)
Definition: circuit.cc:549
std::function< void(Model *)> ExactlyOnePerRowAndPerColumn(const std::vector< std::vector< Literal > > &graph)
Definition: circuit.cc:488
const LiteralIndex kFalseLiteralIndex(-3)
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
Definition: sat_solver.h:907
const BooleanVariable kNoBooleanVariable(-1)
Collection of objects used to extend the Constraint Solver library.
std::pair< int64_t, int64_t > Arc
Definition: search.cc:3434
STL namespace.
Literal literal
Definition: optimization.cc:89
int64_t tail
int64_t head
std::optional< int64_t > end
int64_t start