20#include "absl/container/flat_hash_map.h"
21#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
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>());
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);
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());
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;
131 added_arcs_.resize(level_ends_[level]);
132 level_ends_.resize(level);
135void CircuitPropagator::FillReasonForPath(
int start_node,
136 std::vector<Literal>* reason)
const {
139 int node = start_node;
140 while (next_[node] != -1) {
142 reason->push_back(
Literal(next_literal_[node]).Negated());
145 if (node == start_node)
break;
151void CircuitPropagator::AddArc(
int tail,
int head, LiteralIndex literal_index) {
154 next_literal_[
tail] = literal_index;
162 const std::vector<int>& watch_indices) {
163 for (
const int w : watch_indices) {
165 for (
const Arc arc : watch_index_to_arcs_[w]) {
167 if (
arc.tail ==
arc.head) {
168 must_be_in_cycle_[rev_must_be_in_cycle_size_++] =
arc.tail;
174 if (next_[
arc.tail] != -1) {
180 *conflict = {
literal.Negated()};
184 if (prev_[
arc.head] != -1) {
190 *conflict = {
literal.Negated()};
197 added_arcs_.push_back(
arc);
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;
214 in_current_path_.assign(num_nodes_,
false);
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;
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;
239 if (start_node == end_node && !in_current_path_[0]) {
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;
253 FillReasonForPath(start_node, reason);
268 bool miss_some_nodes =
false;
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();
279 if (miss_some_nodes) {
281 if (start_node == end_node) {
291 if (start_node != end_node) {
292 const auto it = graph_.find({end_node, start_node});
293 if (it == graph_.end())
continue;
298 FillReasonForPath(start_node, reason);
300 reason->push_back(
Literal(extra_reason));
303 if (!ok)
return false;
310 if (start_node != end_node)
continue;
312 for (
int node = 0; node < num_nodes_; ++node) {
313 if (in_current_path_[node])
continue;
332 variable_with_same_reason =
literal.Variable();
335 if (!ok)
return false;
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()),
350 node_is_distinguished_.resize(num_nodes_,
false);
351 for (
const int node : distinguished_nodes) {
352 node_is_distinguished_[node] =
true;
357 const int watcher_id = watcher->
Register(
this);
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];
366 fixed_arcs_.emplace_back(node1, node2);
368 watcher->
WatchLiteral(l, watcher_id, watch_index_to_arc_.size());
369 watch_index_to_arc_.emplace_back(node1, node2);
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());
384 fixed_arcs_.resize(level_ends_[level]);
385 level_ends_.resize(level);
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);
398void CircuitCoveringPropagator::FillFixedPathInReason(
399 int start,
int end, std::vector<Literal>* reason) {
405 reason->push_back(graph_[current][next_[current]].Negated());
406 current = next_[current];
407 }
while (current !=
end);
412 next_.assign(num_nodes_, -1);
413 prev_.assign(num_nodes_, -1);
414 for (
const auto&
arc : fixed_arcs_) {
416 if (next_[
arc.first] != -1) {
418 graph_[
arc.first][next_[
arc.first]].Negated(),
419 graph_[
arc.first][
arc.second].Negated()};
422 next_[
arc.first] =
arc.second;
424 if (prev_[
arc.second] != -1) {
426 graph_[prev_[
arc.second]][
arc.second].Negated(),
427 graph_[
arc.first][
arc.second].Negated()};
430 prev_[
arc.second] =
arc.first;
435 visited_.assign(num_nodes_,
false);
436 for (
int node = 0; node < num_nodes_; node++) {
438 if (visited_[node])
continue;
439 if (prev_[node] == -1 && next_[node] == -1)
continue;
440 if (prev_[node] == node)
continue;
444 for (
int current = prev_[node]; current != -1 && current != node;
445 current = prev_[current]) {
451 int distinguished = node_is_distinguished_[
start] ?
start : -1;
452 int current = next_[
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,
462 distinguished = current;
464 visited_[current] =
true;
466 current = next_[current];
470 if (
start == current && distinguished == -1) {
476 if (current == -1 && distinguished == -1 &&
479 FillFixedPathInReason(
start,
end, reason);
482 if (!ok)
return false;
489 const std::vector<std::vector<Literal>>& graph) {
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]
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) {
512 const int num_arcs = tails.size();
515 CHECK_EQ(literals.size(), num_arcs);
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++) {
526 exactly_one_outgoing[
tail].push_back(literals[
arc]);
527 exactly_one_incoming[
head].push_back(literals[
arc]);
529 for (
int i = 0; i < exactly_one_incoming.size(); ++i) {
530 if (i == 0 && multiple_subcircuit_through_zero)
continue;
532 if (sat_solver->IsModelUnsat())
return;
534 for (
int i = 0; i < exactly_one_outgoing.size(); ++i) {
535 if (i == 0 && multiple_subcircuit_through_zero)
continue;
537 if (sat_solver->IsModelUnsat())
return;
543 num_nodes, tails, heads, literals, options,
model);
545 model->TakeOwnership(constraint);
550 const std::vector<std::vector<Literal>>& graph,
551 const std::vector<int>& distinguished_nodes) {
556 model->TakeOwnership(constraint);
#define DCHECK_NE(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GT(val1, val2)
#define CHECK_NE(val1, val2)
#define DCHECK(condition)
#define VLOG(verboselevel)
An Assignment is a variable -> domains mapping, used to report solutions to the user.
void SetLevel(int level) final
CircuitCoveringPropagator(std::vector< std::vector< Literal > > graph, const std::vector< int > &distinguished_nodes, Model *model)
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.
std::vector< Literal > * GetEmptyVectorToStoreReason(int trail_index) const
void EnqueueWithSameReasonAs(Literal true_literal, BooleanVariable reference_var)
const VariablesAssignment & Assignment() const
ABSL_MUST_USE_RESULT bool EnqueueWithStoredReason(Literal true_literal)
std::vector< Literal > * MutableConflict()
bool LiteralIsTrue(Literal literal) const
bool LiteralIsFalse(Literal literal) const
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)
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)
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
const BooleanVariable kNoBooleanVariable(-1)
Collection of objects used to extend the Constraint Solver library.
std::pair< int64_t, int64_t > Arc
std::optional< int64_t > end
bool multiple_subcircuit_through_zero