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"
36 const std::vector<int>& tails,
37 const std::vector<int>& heads,
38 const std::vector<Literal>& literals,
40 : num_nodes_(num_nodes),
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;
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);
58 graph_.reserve(num_arcs);
60 for (
int arc = 0;
arc < num_arcs; ++
arc) {
73 if (next_[
tail] != -1 || prev_[
head] != -1) {
74 VLOG(1) <<
"Trivially UNSAT or duplicate arcs while adding " <<
tail
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);
93 keys.push_back(watch_index);
98 for (
int node = 0; node < num_nodes_; ++node) {
104 must_be_in_cycle_[rev_must_be_in_cycle_size_++] = node;
111 const int id = watcher->
Register(
this);
112 for (
int w = 0;
w < watch_index_to_literal_.size(); ++
w) {
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());
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;
140 added_arcs_.resize(level_ends_[level]);
141 level_ends_.resize(level);
144void CircuitPropagator::FillReasonForPath(
int start_node,
145 std::vector<Literal>* reason)
const {
146 CHECK_NE(start_node, -1);
148 int node = start_node;
149 while (next_[node] != -1) {
151 reason->push_back(
Literal(next_literal_[node]).Negated());
154 if (node == start_node)
break;
160void CircuitPropagator::AddArc(
int tail,
int head, LiteralIndex literal_index) {
163 next_literal_[
tail] = literal_index;
171 const std::vector<int>& watch_indices) {
172 for (
const int w : watch_indices) {
174 for (
const Arc
arc : watch_index_to_arcs_[
w]) {
176 if (
arc.tail ==
arc.head) {
177 must_be_in_cycle_[rev_must_be_in_cycle_size_++] =
arc.tail;
183 if (next_[
arc.tail] != -1) {
189 *conflict = {
literal.Negated()};
193 if (prev_[
arc.head] != -1) {
199 *conflict = {
literal.Negated()};
206 added_arcs_.push_back(
arc);
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;
223 in_current_path_.assign(num_nodes_,
false);
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;
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;
248 if (start_node == end_node && !in_current_path_[0]) {
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;
262 FillReasonForPath(start_node, reason);
277 bool miss_some_nodes =
false;
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];
288 if (miss_some_nodes) {
290 if (start_node == end_node) {
300 if (start_node != end_node) {
301 const auto it = graph_.find({end_node, start_node});
302 if (it == graph_.end())
continue;
307 FillReasonForPath(start_node, reason);
309 reason->push_back(
Literal(extra_reason));
312 if (!ok)
return false;
319 if (start_node != end_node)
continue;
321 for (
int node = 0; node < num_nodes_; ++node) {
322 if (in_current_path_[node])
continue;
323 if (self_arcs_[node] >= 0 &&
330 CHECK_EQ(next_[node], -1);
347 variable_with_same_reason =
literal.Variable();
350 if (!ok)
return false;
360 const std::vector<int>& tails,
361 const std::vector<int>& heads,
362 const std::vector<Literal>& literals,
364 : num_nodes_(num_nodes),
367 CHECK(!tails.empty()) <<
"Empty constraint, shouldn't be constructed!";
369 graph_.resize(num_nodes);
370 graph_literals_.resize(num_nodes);
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) {
388 const auto [it, inserted] = literal_to_watch_index.insert(
389 {
literal.Index(), watch_index_to_literal_.size()});
391 watch_index_to_literal_.push_back(
literal);
392 watch_index_to_arcs_.push_back({});
394 watch_index_to_arcs_[it->second].push_back({
tail,
head});
405 const int id = watcher->
Register(
this);
406 for (
int w = 0;
w < watch_index_to_literal_.size(); ++
w) {
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());
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();
429 touched_nodes_.resize(level_ends_[level]);
430 level_ends_.resize(level);
434 const std::vector<int>& watch_indices) {
435 for (
const int w : watch_indices) {
437 for (
const auto& [
tail,
head] : watch_index_to_arcs_[
w]) {
440 touched_nodes_.push_back(
tail);
457 for (
const std::vector<int>& compo : components_) {
458 if (compo.size() <= 1)
continue;
466 absl::flat_hash_set<int>
nodes(compo.begin(), compo.end());
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) {
474 conflict->push_back(graph_literals_[
tail][
i].Negated());
485 std::vector<std::vector<Literal>>
graph,
486 const std::vector<int>& distinguished_nodes,
Model*
model)
488 num_nodes_(graph_.
size()),
490 node_is_distinguished_.resize(num_nodes_,
false);
491 for (
const int node : distinguished_nodes) {
492 node_is_distinguished_[node] =
true;
497 const int watcher_id = watcher->
Register(
this);
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];
506 fixed_arcs_.emplace_back(node1, node2);
508 watcher->
WatchLiteral(l, watcher_id, watch_index_to_arc_.size());
509 watch_index_to_arc_.emplace_back(node1, node2);
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());
524 fixed_arcs_.resize(level_ends_[level]);
525 level_ends_.resize(level);
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);
538void CircuitCoveringPropagator::FillFixedPathInReason(
539 int start,
int end, std::vector<Literal>* reason) {
543 DCHECK_NE(next_[current], -1);
545 reason->push_back(graph_[current][next_[current]].Negated());
546 current = next_[current];
547 }
while (current !=
end);
552 next_.assign(num_nodes_, -1);
553 prev_.assign(num_nodes_, -1);
554 for (
const auto&
arc : fixed_arcs_) {
556 if (next_[
arc.first] != -1) {
558 graph_[
arc.first][next_[
arc.first]].Negated(),
559 graph_[
arc.first][
arc.second].Negated()};
562 next_[
arc.first] =
arc.second;
564 if (prev_[
arc.second] != -1) {
566 graph_[prev_[
arc.second]][
arc.second].Negated(),
567 graph_[
arc.first][
arc.second].Negated()};
570 prev_[
arc.second] =
arc.first;
575 visited_.assign(num_nodes_,
false);
576 for (
int node = 0; node < num_nodes_; node++) {
578 if (visited_[node])
continue;
579 if (prev_[node] == -1 && next_[node] == -1)
continue;
580 if (prev_[node] == node)
continue;
584 for (
int current = prev_[node]; current != -1 && current != node;
585 current = prev_[current]) {
591 int distinguished = node_is_distinguished_[
start] ?
start : -1;
592 int current = next_[
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,
602 distinguished = current;
604 visited_[current] =
true;
606 current = next_[current];
610 if (
start == current && distinguished == -1) {
616 if (current == -1 && distinguished == -1 &&
619 FillFixedPathInReason(
start,
end, reason);
622 if (!ok)
return false;
629 const std::vector<std::vector<Literal>>&
graph) {
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]
648 const std::vector<int>& heads,
649 const std::vector<Literal>& literals,
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);
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++) {
667 exactly_one_outgoing[
tail].push_back(literals[
arc]);
668 exactly_one_incoming[
head].push_back(literals[
arc]);
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();
676 sat_solver->AddProblemClause(exactly_one_incoming[
i]);
677 if (sat_solver->ModelIsUnsat())
return;
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();
685 sat_solver->AddProblemClause(exactly_one_outgoing[
i]);
686 if (sat_solver->ModelIsUnsat())
return;
694 model->TakeOwnership(constraint);
698 const std::vector<std::vector<Literal>>&
graph,
699 const std::vector<int>& distinguished_nodes) {
704 model->TakeOwnership(constraint);
CircuitCoveringPropagator(std::vector< std::vector< Literal > > graph, const std::vector< int > &distinguished_nodes, Model *model)
void RegisterWith(GenericLiteralWatcher *watcher)
void SetLevel(int level) final
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
void RegisterWith(GenericLiteralWatcher *watcher)
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
void SetLevel(int level) final
CircuitPropagator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Options options, Model *model)
void ResetFromFlatMapping(Keys keys, Values values)
void RegisterReversibleInt(int id, int *rev)
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)
void WatchLiteral(Literal l, int id, int watch_index=-1)
void RegisterReversibleClass(int id, ReversibleInterface *rev)
int Register(PropagatorInterface *propagator)
Registers a propagator and returns its unique ids.
LiteralIndex Index() const
NoCyclePropagator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Model *model)
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
void SetLevel(int level) final
std::vector< Literal > * MutableConflict()
void EnqueueWithSameReasonAs(Literal true_literal, BooleanVariable reference_var)
std::vector< Literal > * GetEmptyVectorToStoreReason(int trail_index) const
ABSL_MUST_USE_RESULT bool EnqueueWithStoredReason(Literal true_literal)
const VariablesAssignment & Assignment() const
bool LiteralIsFalse(Literal literal) const
bool LiteralIsTrue(Literal literal) const
const LiteralIndex kNoLiteralIndex(-1)
std::function< void(Model *)> CircuitCovering(const std::vector< std::vector< Literal > > &graph, const std::vector< int > &distinguished_nodes)
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)
const LiteralIndex kFalseLiteralIndex(-3)
std::function< void(Model *)> ExactlyOnePerRowAndPerColumn(const std::vector< std::vector< Literal > > &graph)
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
const BooleanVariable kNoBooleanVariable(-1)
In SWIG mode, we don't want anything besides these top-level includes.
trees with all degrees equal w the current value of w
std::optional< int64_t > end
void FindStronglyConnectedComponents(NodeIndex num_nodes, const Graph &graph, SccOutput *components)
Simple wrapper function for most usage.
bool multiple_subcircuit_through_zero