21 #include "absl/container/flat_hash_map.h"
22 #include "absl/meta/type_traits.h"
34 const std::vector<int>& tails,
35 const std::vector<int>& heads,
36 const std::vector<Literal>& literals,
38 : num_nodes_(num_nodes),
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;
49 const int num_arcs = tails.size();
50 graph_.reserve(num_arcs);
51 self_arcs_.resize(num_nodes_,
53 for (
int arc = 0;
arc < num_arcs; ++
arc) {
66 if (next_[
tail] != -1 || prev_[
head] != -1) {
67 VLOG(1) <<
"Trivially UNSAT or duplicate arcs while adding " <<
tail
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>());
86 watch_index_to_arcs_[watch_index].push_back({
tail,
head});
89 for (
int node = 0; node < num_nodes_; ++node) {
94 must_be_in_cycle_[rev_must_be_in_cycle_size_++] = node;
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);
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());
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;
130 added_arcs_.resize(level_ends_[level]);
131 level_ends_.resize(level);
134 void CircuitPropagator::FillReasonForPath(
int start_node,
135 std::vector<Literal>* reason)
const {
136 CHECK_NE(start_node, -1);
138 int node = start_node;
139 while (next_[node] != -1) {
141 reason->push_back(
Literal(next_literal_[node]).Negated());
144 if (node == start_node)
break;
150 void CircuitPropagator::AddArc(
int tail,
int head, LiteralIndex literal_index) {
153 next_literal_[
tail] = literal_index;
161 const std::vector<int>& watch_indices) {
162 for (
const int w : watch_indices) {
164 for (
const Arc
arc : watch_index_to_arcs_[w]) {
166 if (
arc.tail ==
arc.head) {
167 must_be_in_cycle_[rev_must_be_in_cycle_size_++] =
arc.tail;
173 if (next_[
arc.tail] != -1) {
179 *conflict = {
literal.Negated()};
183 if (prev_[
arc.head] != -1) {
189 *conflict = {
literal.Negated()};
196 added_arcs_.push_back(
arc);
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;
213 in_current_path_.assign(num_nodes_,
false);
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;
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;
238 if (start_node == end_node && !in_current_path_[0]) {
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;
252 FillReasonForPath(start_node, reason);
267 bool miss_some_nodes =
false;
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();
278 if (miss_some_nodes) {
280 if (start_node == end_node) {
290 if (start_node != end_node) {
291 const auto it = graph_.find({end_node, start_node});
292 if (it == graph_.end())
continue;
297 FillReasonForPath(start_node, reason);
299 reason->push_back(
Literal(extra_reason));
302 if (!ok)
return false;
309 if (start_node != end_node)
continue;
311 for (
int node = 0; node < num_nodes_; ++node) {
312 if (in_current_path_[node])
continue;
317 CHECK_EQ(next_[node], -1);
331 variable_with_same_reason =
literal.Variable();
334 if (!ok)
return false;
344 const std::vector<int>& tails,
345 const std::vector<int>& heads,
346 const std::vector<Literal>& literals,
348 : num_nodes_(num_nodes),
351 CHECK(!tails.empty()) <<
"Empty constraint, shouldn't be constructed!";
353 graph_.resize(num_nodes);
354 graph_literals_.resize(num_nodes);
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) {
372 const auto [it, inserted] = literal_to_watch_index.insert(
373 {
literal.Index(), watch_index_to_literal_.size()});
375 watch_index_to_literal_.push_back(
literal);
376 watch_index_to_arcs_.push_back({});
378 watch_index_to_arcs_[it->second].push_back({
tail,
head});
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);
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());
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();
413 touched_nodes_.resize(level_ends_[level]);
414 level_ends_.resize(level);
418 const std::vector<int>& watch_indices) {
419 for (
const int w : watch_indices) {
421 for (
const auto& [
tail,
head] : watch_index_to_arcs_[w]) {
424 touched_nodes_.push_back(
tail);
441 for (
const std::vector<int>& compo : components_) {
442 if (compo.size() <= 1)
continue;
450 absl::flat_hash_set<int>
nodes(compo.begin(), compo.end());
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) {
458 conflict->push_back(graph_literals_[
tail][i].Negated());
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()),
474 node_is_distinguished_.resize(num_nodes_,
false);
475 for (
const int node : distinguished_nodes) {
476 node_is_distinguished_[node] =
true;
481 const int watcher_id = watcher->
Register(
this);
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];
490 fixed_arcs_.emplace_back(node1, node2);
492 watcher->
WatchLiteral(l, watcher_id, watch_index_to_arc_.size());
493 watch_index_to_arc_.emplace_back(node1, node2);
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());
508 fixed_arcs_.resize(level_ends_[level]);
509 level_ends_.resize(level);
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);
522 void CircuitCoveringPropagator::FillFixedPathInReason(
523 int start,
int end, std::vector<Literal>* reason) {
527 DCHECK_NE(next_[current], -1);
529 reason->push_back(graph_[current][next_[current]].Negated());
530 current = next_[current];
531 }
while (current !=
end);
536 next_.assign(num_nodes_, -1);
537 prev_.assign(num_nodes_, -1);
538 for (
const auto&
arc : fixed_arcs_) {
540 if (next_[
arc.first] != -1) {
542 graph_[
arc.first][next_[
arc.first]].Negated(),
543 graph_[
arc.first][
arc.second].Negated()};
546 next_[
arc.first] =
arc.second;
548 if (prev_[
arc.second] != -1) {
550 graph_[prev_[
arc.second]][
arc.second].Negated(),
551 graph_[
arc.first][
arc.second].Negated()};
554 prev_[
arc.second] =
arc.first;
559 visited_.assign(num_nodes_,
false);
560 for (
int node = 0; node < num_nodes_; node++) {
562 if (visited_[node])
continue;
563 if (prev_[node] == -1 && next_[node] == -1)
continue;
564 if (prev_[node] == node)
continue;
568 for (
int current = prev_[node]; current != -1 && current != node;
569 current = prev_[current]) {
575 int distinguished = node_is_distinguished_[
start] ?
start : -1;
576 int current = next_[
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,
586 distinguished = current;
588 visited_[current] =
true;
590 current = next_[current];
594 if (
start == current && distinguished == -1) {
600 if (current == -1 && distinguished == -1 &&
603 FillFixedPathInReason(
start,
end, reason);
606 if (!ok)
return false;
613 const std::vector<std::vector<Literal>>& graph) {
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]
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) {
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);
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++) {
650 exactly_one_outgoing[
tail].push_back(literals[
arc]);
651 exactly_one_incoming[
head].push_back(literals[
arc]);
653 for (
int i = 0; i < exactly_one_incoming.size(); ++i) {
654 if (i == 0 && multiple_subcircuit_through_zero)
continue;
656 if (sat_solver->ModelIsUnsat())
return;
658 for (
int i = 0; i < exactly_one_outgoing.size(); ++i) {
659 if (i == 0 && multiple_subcircuit_through_zero)
continue;
661 if (sat_solver->ModelIsUnsat())
return;
667 num_nodes, tails, heads, literals, options,
model);
669 model->TakeOwnership(constraint);
674 const std::vector<std::vector<Literal>>& graph,
675 const std::vector<int>& distinguished_nodes) {
680 model->TakeOwnership(constraint);
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)
void SetLevel(int level) final
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
void RegisterWith(GenericLiteralWatcher *watcher)
void SetLevel(int level) final
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
void RegisterWith(GenericLiteralWatcher *watcher)
CircuitPropagator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Options options, Model *model)
void RegisterReversibleInt(int id, int *rev)
void RegisterReversibleClass(int id, ReversibleInterface *rev)
void WatchLiteral(Literal l, int id, int watch_index=-1)
int Register(PropagatorInterface *propagator)
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)
Literal GetFalseLiteral()
LiteralIndex Index() const
Class that owns everything related to a particular optimization model.
void SetLevel(int level) final
bool IncrementalPropagate(const std::vector< int > &watch_indices) final
NoCyclePropagator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Model *model)
void EnqueueWithSameReasonAs(Literal true_literal, BooleanVariable reference_var)
std::vector< Literal > * GetEmptyVectorToStoreReason(int trail_index) const
std::vector< Literal > * MutableConflict()
const VariablesAssignment & Assignment() const
ABSL_MUST_USE_RESULT bool EnqueueWithStoredReason(Literal true_literal)
bool LiteralIsTrue(Literal literal) const
bool LiteralIsFalse(Literal literal) const
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
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)
std::function< void(Model *)> CircuitCovering(const std::vector< std::vector< Literal >> &graph, const std::vector< int > &distinguished_nodes)
std::function< void(Model *)> ExactlyOnePerRowAndPerColumn(const std::vector< std::vector< Literal >> &graph)
const LiteralIndex kFalseLiteralIndex(-3)
const BooleanVariable kNoBooleanVariable(-1)
Collection of objects used to extend the Constraint Solver library.
std::optional< int64_t > end
void FindStronglyConnectedComponents(const NodeIndex num_nodes, const Graph &graph, SccOutput *components)
bool multiple_subcircuit_through_zero
#define VLOG(verboselevel)