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