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/types/span.h"
40 const int num_nodes, absl::Span<const int> tails,
41 absl::Span<const int> heads, absl::Span<const Literal> enforcement_literals,
42 absl::Span<const Literal> literals,
Options options,
Model* model)
43 : num_nodes_(num_nodes),
45 trail_(*model->GetOrCreate<
Trail>()),
48 CHECK(!tails.empty()) <<
"Empty constraint, shouldn't be constructed!";
49 next_.resize(num_nodes_, -1);
50 prev_.resize(num_nodes_, -1);
51 next_literal_.resize(num_nodes_);
52 must_be_in_cycle_.resize(num_nodes_);
53 absl::flat_hash_map<LiteralIndex, int> literal_to_watch_index;
56 const int num_arcs = tails.size();
57 std::vector<int> keys;
58 std::vector<Arc> values;
59 keys.reserve(num_arcs);
60 values.reserve(num_arcs);
62 graph_.reserve(num_arcs);
65 for (
int arc = 0; arc < num_arcs; ++arc) {
66 const int head = heads[arc];
67 const int tail = tails[arc];
68 const Literal literal = literals[arc];
69 if (assignment_.LiteralIsFalse(literal))
continue;
72 self_arcs_[tail] = literal.
Index();
74 graph_[{tail, head}] = literal;
77 if (assignment_.LiteralIsTrue(literal)) {
78 if (next_[tail] != -1 || prev_[head] != -1) {
80 if (enforcement_literals.empty()) {
81 VLOG(1) <<
"Trivially UNSAT or duplicate arcs while adding " << tail
85 std::vector<Literal> negated_enforcement_literals;
86 for (
const Literal literal : enforcement_literals) {
87 negated_enforcement_literals.push_back(literal.
Negated());
99 const Literal watched_literal = tail == head ? literal.
Negated() : literal;
100 const auto& it = literal_to_watch_index.find(watched_literal.
Index());
101 int watch_index = it != literal_to_watch_index.end() ? it->second : -1;
102 if (watch_index == -1) {
103 watch_index = watch_index_to_literal_.size();
104 literal_to_watch_index[watched_literal.
Index()] = watch_index;
105 watch_index_to_literal_.push_back(watched_literal);
108 keys.push_back(watch_index);
109 values.push_back({tail, head});
111 watch_index_to_arcs_.ResetFromFlatMapping(keys, values);
113 for (
int node = 0; node < num_nodes_; ++node) {
115 assignment_.LiteralIsFalse(
Literal(self_arcs_[node]))) {
118 if (node == 0 || !options_.multiple_subcircuit_through_zero) {
119 must_be_in_cycle_[rev_must_be_in_cycle_size_++] = node;
125 enforcement_id_ = enforcement_helper_.
Register(enforcement_literals, watcher,
126 RegisterWith(watcher));
130 const int id = watcher->
Register(
this);
131 for (
int w = 0;
w < watch_index_to_literal_.size(); ++
w) {
146 if (!enabled_)
return;
147 if (level == level_ends_.size())
return;
148 if (level > level_ends_.size()) {
149 while (level > level_ends_.size()) {
150 level_ends_.push_back(added_arcs_.size());
156 for (
int i = level_ends_[level];
i < added_arcs_.size(); ++
i) {
157 const Arc arc = added_arcs_[
i];
158 next_[arc.tail] = -1;
159 prev_[arc.head] = -1;
161 added_arcs_.resize(level_ends_[level]);
162 level_ends_.resize(level);
165void CircuitPropagator::FillReasonForPath(
int start_node,
166 std::vector<Literal>* reason)
const {
167 CHECK_NE(start_node, -1);
169 int node = start_node;
170 while (next_[node] != -1) {
172 reason->push_back(
Literal(next_literal_[node]).Negated());
175 if (node == start_node)
break;
179bool CircuitPropagator::ReportConflictOrPropagateEnforcement(
180 std::vector<Literal>* reason) {
181 if (enforcement_helper_.Status(enforcement_id_) ==
183 enforcement_helper_.AddEnforcementReason(enforcement_id_, reason);
184 trail_.MutableConflict()->assign(reason->begin(), reason->end());
187 return enforcement_helper_.PropagateWhenFalse(enforcement_id_, *reason,
194void CircuitPropagator::AddArc(
int tail,
int head, LiteralIndex literal_index) {
195 if (tail != 0 || !options_.multiple_subcircuit_through_zero) {
197 next_literal_[tail] = literal_index;
199 if (head != 0 || !options_.multiple_subcircuit_through_zero) {
205 const std::vector<int>& watch_indices) {
206 if (!enabled_)
return true;
213 for (
const int w : watch_indices) {
214 const Literal literal = watch_index_to_literal_[
w];
215 for (
const Arc arc : watch_index_to_arcs_[
w]) {
217 if (arc.tail == arc.head) {
218 must_be_in_cycle_[rev_must_be_in_cycle_size_++] = arc.tail;
224 if (next_[arc.tail] != -1) {
229 temp_reason_ = {literal.
Negated()};
231 return ReportConflictOrPropagateEnforcement(&temp_reason_);
233 if (prev_[arc.head] != -1) {
235 temp_reason_ = {
Literal(next_literal_[prev_[arc.head]]).
Negated(),
238 temp_reason_ = {literal.
Negated()};
240 return ReportConflictOrPropagateEnforcement(&temp_reason_);
244 AddArc(arc.tail, arc.head, literal.
Index());
245 added_arcs_.push_back(arc);
254 if (!enabled_)
return true;
261 processed_.assign(num_nodes_,
false);
262 for (
int n = 0; n < num_nodes_; ++n) {
263 if (processed_[n])
continue;
264 if (next_[n] == n)
continue;
265 if (next_[n] == -1 && prev_[n] == -1)
continue;
269 in_current_path_.assign(num_nodes_,
false);
275 in_current_path_[n] =
true;
276 processed_[n] =
true;
277 while (next_[end_node] != -1) {
278 end_node = next_[end_node];
279 in_current_path_[end_node] =
true;
280 processed_[end_node] =
true;
281 if (end_node == n)
break;
283 while (prev_[start_node] != -1) {
284 start_node = prev_[start_node];
285 in_current_path_[start_node] =
true;
286 processed_[start_node] =
true;
287 if (start_node == n)
break;
292 if (options_.multiple_subcircuit_through_zero) {
294 if (start_node == end_node && !in_current_path_[0]) {
295 FillReasonForPath(start_node, &temp_reason_);
296 return ReportConflictOrPropagateEnforcement(&temp_reason_);
301 if (start_node != end_node && start_node != 0 && end_node != 0 &&
303 const auto it = graph_.find({end_node, start_node});
304 if (it == graph_.end())
continue;
305 const Literal literal = it->second;
306 if (assignment_.LiteralIsFalse(literal))
continue;
308 std::vector<Literal>* reason = trail_.GetEmptyVectorToStoreReason();
309 FillReasonForPath(start_node, reason);
310 enforcement_helper_.AddEnforcementReason(enforcement_id_, reason);
325 bool miss_some_nodes =
false;
327 for (
int i = 0;
i < rev_must_be_in_cycle_size_; ++
i) {
328 const int node = must_be_in_cycle_[
i];
329 if (!in_current_path_[node]) {
330 miss_some_nodes =
true;
331 extra_reason = self_arcs_[node];
336 if (miss_some_nodes) {
338 if (start_node == end_node) {
339 FillReasonForPath(start_node, &temp_reason_);
341 temp_reason_.push_back(
Literal(extra_reason));
343 return ReportConflictOrPropagateEnforcement(&temp_reason_);
349 const auto it = graph_.find({end_node, start_node});
350 if (it == graph_.end())
continue;
351 const Literal literal = it->second;
352 if (assignment_.LiteralIsFalse(literal))
continue;
354 std::vector<Literal>* reason = trail_.GetEmptyVectorToStoreReason();
355 FillReasonForPath(start_node, reason);
356 enforcement_helper_.AddEnforcementReason(enforcement_id_, reason);
358 reason->push_back(
Literal(extra_reason));
362 if (!ok)
return false;
369 if (start_node != end_node)
continue;
371 for (
int node = 0; node < num_nodes_; ++node) {
372 if (in_current_path_[node])
continue;
373 if (self_arcs_[node] >= 0 &&
374 assignment_.LiteralIsTrue(
Literal(self_arcs_[node]))) {
380 CHECK_EQ(next_[node], -1);
386 assignment_.LiteralIsFalse(
Literal(self_arcs_[node]))) {
387 FillReasonForPath(start_node, &temp_reason_);
389 temp_reason_.push_back(
Literal(self_arcs_[node]));
391 return ReportConflictOrPropagateEnforcement(&temp_reason_);
396 const Literal literal(self_arcs_[node]);
398 variable_with_same_reason = literal.
Variable();
399 std::vector<Literal>* reason = trail_.GetEmptyVectorToStoreReason();
400 FillReasonForPath(start_node, reason);
401 enforcement_helper_.AddEnforcementReason(enforcement_id_, reason);
402 const bool ok = trail_.EnqueueWithStoredReason(
kNoClauseId, literal);
403 if (!ok)
return false;
405 trail_.EnqueueWithSameReasonAs(literal, variable_with_same_reason);
414 absl::Span<const int> heads,
415 absl::Span<const Literal> literals,
417 : num_nodes_(num_nodes),
418 trail_(model->GetOrCreate<
Trail>()),
420 CHECK(!tails.empty()) <<
"Empty constraint, shouldn't be constructed!";
422 graph_.resize(num_nodes);
423 graph_literals_.resize(num_nodes);
425 const int num_arcs = tails.size();
426 absl::flat_hash_map<LiteralIndex, int> literal_to_watch_index;
427 for (
int arc = 0; arc < num_arcs; ++arc) {
428 const int head = heads[arc];
429 const int tail = tails[arc];
430 const Literal literal = literals[arc];
432 if (assignment_.LiteralIsFalse(literal))
continue;
433 if (assignment_.LiteralIsTrue(literal)) {
435 graph_[tail].push_back(head);
436 graph_literals_[tail].push_back(literal);
441 const auto [it, inserted] = literal_to_watch_index.insert(
442 {literal.
Index(), watch_index_to_literal_.size()});
444 watch_index_to_literal_.push_back(literal);
445 watch_index_to_arcs_.push_back({});
447 watch_index_to_arcs_[it->second].push_back({tail, head});
458 const int id = watcher->
Register(
this);
459 for (
int w = 0;
w < watch_index_to_literal_.size(); ++
w) {
469 if (level == level_ends_.size())
return;
470 if (level > level_ends_.size()) {
471 while (level > level_ends_.size()) {
472 level_ends_.push_back(touched_nodes_.size());
478 for (
int i = level_ends_[level];
i < touched_nodes_.size(); ++
i) {
479 graph_literals_[touched_nodes_[
i]].pop_back();
480 graph_[touched_nodes_[
i]].pop_back();
482 touched_nodes_.resize(level_ends_[level]);
483 level_ends_.resize(level);
487 const std::vector<int>& watch_indices) {
488 for (
const int w : watch_indices) {
489 const Literal literal = watch_index_to_literal_[
w];
490 for (
const auto& [tail, head] : watch_index_to_arcs_[
w]) {
491 graph_[tail].push_back(head);
492 graph_literals_[tail].push_back(literal);
493 touched_nodes_.push_back(tail);
510 for (
const std::vector<int>& compo : components_) {
511 if (compo.size() <= 1)
continue;
519 absl::flat_hash_set<int> nodes(compo.begin(), compo.end());
520 std::vector<Literal>* conflict = trail_->MutableConflict();
522 for (
const int tail : compo) {
523 const int degree = graph_[tail].size();
524 CHECK_EQ(degree, graph_literals_[tail].size());
525 for (
int i = 0;
i < degree; ++
i) {
526 if (nodes.contains(graph_[tail][
i])) {
527 conflict->push_back(graph_literals_[tail][
i].Negated());
538 std::vector<std::vector<Literal>> graph,
539 absl::Span<const int> distinguished_nodes,
Model* model)
540 : graph_(
std::move(graph)),
541 num_nodes_(graph_.size()),
542 trail_(model->GetOrCreate<
Trail>()) {
543 node_is_distinguished_.resize(num_nodes_,
false);
544 for (
const int node : distinguished_nodes) {
545 node_is_distinguished_[node] =
true;
550 const int watcher_id = watcher->
Register(
this);
554 for (
int node1 = 0; node1 < num_nodes_; node1++) {
555 for (
int node2 = 0; node2 < num_nodes_; node2++) {
556 const Literal l = graph_[node1][node2];
557 if (trail_->Assignment().LiteralIsFalse(l))
continue;
558 if (trail_->Assignment().LiteralIsTrue(l)) {
559 fixed_arcs_.emplace_back(node1, node2);
561 watcher->
WatchLiteral(l, watcher_id, watch_index_to_arc_.size());
562 watch_index_to_arc_.emplace_back(node1, node2);
570 if (level == level_ends_.size())
return;
571 if (level > level_ends_.size()) {
572 while (level > level_ends_.size()) {
573 level_ends_.push_back(fixed_arcs_.size());
577 fixed_arcs_.resize(level_ends_[level]);
578 level_ends_.resize(level);
583 const std::vector<int>& watch_indices) {
584 for (
const int w : watch_indices) {
585 const auto& arc = watch_index_to_arc_[
w];
586 fixed_arcs_.push_back(arc);
591void CircuitCoveringPropagator::FillFixedPathInReason(
592 int start,
int end, std::vector<Literal>* reason) {
596 DCHECK_NE(next_[current], -1);
598 reason->push_back(graph_[current][next_[current]].Negated());
599 current = next_[current];
600 }
while (current !=
end);
605 next_.assign(num_nodes_, -1);
606 prev_.assign(num_nodes_, -1);
607 for (
const auto& arc : fixed_arcs_) {
609 if (next_[arc.first] != -1) {
610 *trail_->MutableConflict() = {
611 graph_[arc.first][next_[arc.first]].Negated(),
612 graph_[arc.first][arc.second].Negated()};
615 next_[arc.first] = arc.second;
617 if (prev_[arc.second] != -1) {
618 *trail_->MutableConflict() = {
619 graph_[prev_[arc.second]][arc.second].Negated(),
620 graph_[arc.first][arc.second].Negated()};
623 prev_[arc.second] = arc.first;
628 visited_.assign(num_nodes_,
false);
629 for (
int node = 0; node < num_nodes_; node++) {
631 if (visited_[node])
continue;
632 if (prev_[node] == -1 && next_[node] == -1)
continue;
633 if (prev_[node] == node)
continue;
637 for (
int current = prev_[node]; current != -1 && current != node;
638 current = prev_[current]) {
644 int distinguished = node_is_distinguished_[start] ? start : -1;
645 int current = next_[start];
647 visited_[start] =
true;
648 while (current != -1 && current != start) {
649 if (node_is_distinguished_[current]) {
650 if (distinguished != -1) {
651 FillFixedPathInReason(distinguished, current,
652 trail_->MutableConflict());
655 distinguished = current;
657 visited_[current] =
true;
659 current = next_[current];
663 if (start == current && distinguished == -1) {
664 FillFixedPathInReason(start, start, trail_->MutableConflict());
669 if (current == -1 && distinguished == -1 &&
670 !trail_->Assignment().LiteralIsFalse(graph_[
end][start])) {
671 auto* reason = trail_->GetEmptyVectorToStoreReason();
672 FillFixedPathInReason(start,
end, reason);
673 const bool ok = trail_->EnqueueWithStoredReason(
675 if (!ok)
return false;
682 absl::Span<
const std::vector<Literal>> graph) {
683 return [=, graph = std::vector<std::vector<Literal>>(
684 graph.begin(), graph.end())](
Model* model) {
685 const int n = graph.size();
686 std::vector<Literal> exactly_one_constraint;
687 exactly_one_constraint.reserve(n);
688 for (
const bool transpose : {
false,
true}) {
689 for (
int i = 0;
i < n; ++
i) {
690 exactly_one_constraint.clear();
691 for (
int j = 0; j < n; ++j) {
692 exactly_one_constraint.push_back(transpose ? graph[j][
i]
702bool AddAtMostOne(absl::Span<const Literal> enforcement_literals,
703 absl::Span<const Literal> literals,
Model* model) {
704 if (enforcement_literals.empty()) {
705 return model->GetOrCreate<BinaryImplicationGraph>()->AddAtMostOne(literals);
707 std::vector<Literal> enforcement(enforcement_literals.begin(),
708 enforcement_literals.end());
709 std::vector<LiteralWithCoeff> cst;
710 cst.reserve(literals.size());
711 for (
const Literal l : literals) {
712 cst.emplace_back(l, Coefficient(1));
714 return model->GetOrCreate<
SatSolver>()->AddLinearConstraint(
721 absl::Span<const int> heads,
722 absl::Span<const Literal> enforcement_literals,
723 absl::Span<const Literal> literals,
Model* model,
724 bool multiple_subcircuit_through_zero) {
725 const int num_arcs = tails.size();
726 CHECK_GT(num_arcs, 0);
727 CHECK_EQ(heads.size(), num_arcs);
728 CHECK_EQ(literals.size(), num_arcs);
734 std::vector<std::vector<Literal>> exactly_one_incoming(num_nodes);
735 std::vector<std::vector<Literal>> exactly_one_outgoing(num_nodes);
736 for (
int arc = 0; arc < num_arcs; arc++) {
737 const int tail = tails[arc];
738 const int head = heads[arc];
739 exactly_one_outgoing[tail].push_back(literals[arc]);
740 exactly_one_incoming[head].push_back(literals[arc]);
742 for (
int i = 0;
i < exactly_one_incoming.size(); ++
i) {
743 if (
i == 0 && multiple_subcircuit_through_zero)
continue;
744 if (!AddAtMostOne(enforcement_literals, exactly_one_incoming[
i], model)) {
745 sat_solver->NotifyThatModelIsUnsat();
749 if (sat_solver->ModelIsUnsat())
return;
751 for (
int i = 0;
i < exactly_one_outgoing.size(); ++
i) {
752 if (
i == 0 && multiple_subcircuit_through_zero)
continue;
753 if (!AddAtMostOne(enforcement_literals, exactly_one_outgoing[
i], model)) {
754 sat_solver->NotifyThatModelIsUnsat();
758 if (sat_solver->ModelIsUnsat())
return;
764 num_nodes, tails, heads, enforcement_literals, literals, options, model));
771 enforcement_literals.empty() && !multiple_subcircuit_through_zero) {
780 absl::Span<
const std::vector<Literal>> graph,
781 absl::Span<const int> distinguished_nodes) {
783 distinguished_nodes = std::vector<int>(distinguished_nodes.begin(),
784 distinguished_nodes.end()),
785 graph = std::vector<std::vector<Literal>>(
786 graph.begin(), graph.end())](
Model* model) {
790 model->TakeOwnership(constraint);
void RegisterWith(GenericLiteralWatcher *watcher)
CircuitCoveringPropagator(std::vector< std::vector< Literal > > graph, absl::Span< const int > distinguished_nodes, Model *model)
void RegisterWith(GenericLiteralWatcher *watcher)
void SetLevel(int level) final
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
void SetLevel(int level) final
CircuitPropagator(int num_nodes, absl::Span< const int > tails, absl::Span< const int > heads, absl::Span< const Literal > enforcement_literals, absl::Span< const Literal > literals, Options options, Model *model)
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)
LiteralIndex Index() const
BooleanVariable Variable() const
T Add(std::function< T(Model *)> f)
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
NoCyclePropagator(int num_nodes, absl::Span< const int > tails, absl::Span< const int > heads, absl::Span< const Literal > literals, Model *model)
void SetLevel(int level) final
bool use_all_different_for_circuit() const
bool AddProblemClause(absl::Span< const Literal > literals, bool shared=false)
void NotifyThatModelIsUnsat()
const VariablesAssignment & Assignment() const
bool LiteralIsTrue(Literal literal) const
std::tuple< int64_t, int64_t, const double > Coefficient
const LiteralIndex kNoLiteralIndex(-1)
std::function< void(Model *)> EnforcedClause(absl::Span< const Literal > enforcement_literals, absl::Span< const Literal > clause)
std::function< void(Model *)> CircuitCovering(absl::Span< const std::vector< Literal > > graph, absl::Span< const int > distinguished_nodes)
constexpr ClauseId kNoClauseId(0)
void LoadSubcircuitConstraint(int num_nodes, absl::Span< const int > tails, absl::Span< const int > heads, absl::Span< const Literal > enforcement_literals, absl::Span< const Literal > literals, Model *model, bool multiple_subcircuit_through_zero)
const LiteralIndex kFalseLiteralIndex(-3)
@ CAN_PROPAGATE_ENFORCEMENT
std::function< void(Model *)> ExactlyOneConstraint(absl::Span< const Literal > literals)
std::function< void(Model *)> ExactlyOnePerRowAndPerColumn(absl::Span< const std::vector< Literal > > graph)
const BooleanVariable kNoBooleanVariable(-1)
ClosedInterval::Iterator end(ClosedInterval interval)
trees with all degrees equal w the current value of w
void FindStronglyConnectedComponents(NodeIndex num_nodes, const Graph &graph, SccOutput *components)
bool multiple_subcircuit_through_zero