38 int num_nodes,
int subset_size,
const std::vector<bool>& in_subset,
39 const std::vector<int>& tails,
const std::vector<int>& heads,
40 const std::vector<Literal>& literals,
41 const std::vector<double>& literal_lp_values, int64_t rhs_lower_bound,
43 LinearConstraintManager* manager, Model*
model) {
50 int num_optional_nodes_in = 0;
51 int num_optional_nodes_out = 0;
52 int optional_loop_in = -1;
53 int optional_loop_out = -1;
54 for (
int i = 0; i < tails.size(); ++i) {
55 if (tails[i] != heads[i])
continue;
56 if (in_subset[tails[i]]) {
57 num_optional_nodes_in++;
58 if (optional_loop_in == -1 ||
59 literal_lp_values[i] < literal_lp_values[optional_loop_in]) {
63 num_optional_nodes_out++;
64 if (optional_loop_out == -1 ||
65 literal_lp_values[i] < literal_lp_values[optional_loop_out]) {
66 optional_loop_out = i;
73 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
74 CHECK_GE(rhs_lower_bound, 1);
78 LinearConstraintBuilder outgoing(
model, IntegerValue(rhs_lower_bound),
80 double sum_outgoing = 0.0;
83 for (
int i = 0; i < tails.size(); ++i) {
84 if (in_subset[tails[i]] && !in_subset[heads[i]]) {
85 sum_outgoing += literal_lp_values[i];
86 CHECK(outgoing.AddLiteralTerm(literals[i], IntegerValue(1)));
91 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
93 if (num_optional_nodes_in == subset_size &&
94 (optional_loop_in == -1 ||
95 literal_lp_values[optional_loop_in] > 1.0 - 1e-6)) {
98 if (num_optional_nodes_out == num_nodes - subset_size &&
99 (optional_loop_out == -1 ||
100 literal_lp_values[optional_loop_out] > 1.0 - 1e-6)) {
105 if (num_optional_nodes_in == subset_size) {
107 outgoing.AddLiteralTerm(literals[optional_loop_in], IntegerValue(1)));
108 sum_outgoing += literal_lp_values[optional_loop_in];
112 if (num_optional_nodes_out == num_nodes - subset_size) {
113 CHECK(outgoing.AddLiteralTerm(literals[optional_loop_out],
115 sum_outgoing += literal_lp_values[optional_loop_out];
119 if (sum_outgoing < rhs_lower_bound - 1e-6) {
120 manager->AddCut(outgoing.Build(),
"Circuit", lp_values);
127 const std::vector<std::pair<int, int>>& arcs,
128 int min_subset_size,
int stop_at_num_components,
129 std::vector<int>* subset_data,
130 std::vector<absl::Span<const int>>* subsets) {
131 subset_data->resize(num_nodes);
142 int num_components = num_nodes;
143 std::vector<int> parent(num_nodes);
144 std::vector<int> root(num_nodes);
145 for (
int i = 0; i < num_nodes; ++i) {
149 auto get_root_and_compress_path = [&root](
int node) {
151 while (root[r] != r) r = root[r];
152 while (root[node] != r) {
153 const int next = root[node];
159 for (
const auto& [initial_tail, initial_head] : arcs) {
160 if (num_components <= stop_at_num_components)
break;
161 const int tail = get_root_and_compress_path(initial_tail);
162 const int head = get_root_and_compress_path(initial_head);
166 const int new_node = parent.size();
167 parent.push_back(new_node);
168 parent[
head] = new_node;
169 parent[
tail] = new_node;
173 root.push_back(new_node);
174 root[
head] = new_node;
175 root[
tail] = new_node;
187 std::vector<absl::InlinedVector<int, 2>> graph(parent.size());
188 for (
int i = 0; i < parent.size(); ++i) {
189 if (parent[i] != i) graph[parent[i]].push_back(i);
191 std::vector<int> queue;
192 std::vector<bool> seen(graph.size(),
false);
193 std::vector<int> start_index(parent.size());
194 for (
int i = 0; i < parent.size(); ++i) {
198 CHECK(graph[i].empty() || graph[i].size() == 2);
199 if (parent[i] != i)
continue;
204 while (!queue.empty()) {
205 const int node = queue.back();
210 const int start = start_index[node];
211 if (new_size -
start >= min_subset_size) {
212 subsets->emplace_back(&(*subset_data)[
start], new_size -
start);
217 start_index[node] = new_size;
218 if (node < num_nodes) (*subset_data)[new_size++] = node;
219 for (
const int child : graph[node]) {
220 if (!seen[child]) queue.push_back(child);
226 DCHECK_EQ(new_size, num_nodes);
236 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
237 const std::vector<Literal>& literals,
239 absl::Span<const int64_t> demands, int64_t
capacity,
241 if (num_nodes <= 2)
return;
250 std::vector<Arc> relevant_arcs;
253 std::vector<double> literal_lp_values(literals.size());
254 std::vector<std::pair<double, int>> arc_by_decreasing_lp_values;
256 for (
int i = 0; i < literals.size(); ++i) {
258 const IntegerVariable direct_view = encoder->
GetLiteralView(literals[i]);
260 lp_value = lp_values[direct_view];
263 1.0 - lp_values[encoder->GetLiteralView(literals[i].Negated())];
265 literal_lp_values[i] = lp_value;
267 if (lp_value < 1e-6)
continue;
268 relevant_arcs.
push_back({tails[i], heads[i], lp_value});
269 arc_by_decreasing_lp_values.push_back({lp_value, i});
271 std::sort(arc_by_decreasing_lp_values.begin(),
272 arc_by_decreasing_lp_values.end(),
273 std::greater<std::pair<double, int>>());
275 std::vector<std::pair<int, int>> ordered_arcs;
276 for (
const auto& [score,
arc] : arc_by_decreasing_lp_values) {
277 ordered_arcs.push_back({tails[
arc], heads[
arc]});
279 std::vector<int> subset_data;
280 std::vector<absl::Span<const int>> subsets;
287 if (!demands.empty()) {
290 subsets.push_back(absl::MakeSpan(&depot, 1));
295 int64_t total_demands = 0;
296 if (!demands.empty()) {
297 for (
const int64_t
demand : demands) total_demands +=
demand;
301 std::vector<bool> in_subset(num_nodes,
false);
302 for (
const absl::Span<const int> subset : subsets) {
303 DCHECK_GE(subset.size(), 1);
304 DCHECK_LT(subset.size(), num_nodes);
307 bool contain_depot =
false;
308 int64_t subset_demand = 0;
311 for (
const int n : subset) {
313 if (!demands.empty()) {
314 if (n == 0) contain_depot =
true;
315 subset_demand += demands[n];
332 int64_t min_outgoing_flow = 1;
333 if (!demands.empty()) {
342 min_outgoing_flow =
std::max(min_outgoing_flow, int64_t{1});
354 double outgoing_flow = 0.0;
355 for (
const auto arc : relevant_arcs) {
356 if (in_subset[
arc.tail] && !in_subset[
arc.head]) {
357 outgoing_flow +=
arc.lp_value;
362 if (outgoing_flow < min_outgoing_flow - 1e-6) {
363 AddOutgoingCut(num_nodes, subset.size(), in_subset, tails, heads,
364 literals, literal_lp_values,
365 min_outgoing_flow, lp_values, manager,
370 for (
const int n : subset) in_subset[n] =
false;
377 std::vector<IntegerVariable> GetAssociatedVariables(
378 const std::vector<Literal>& literals, Model*
model) {
379 auto* encoder =
model->GetOrCreate<IntegerEncoder>();
380 std::vector<IntegerVariable> result;
381 for (
const Literal l : literals) {
382 const IntegerVariable direct_view = encoder->GetLiteralView(l);
384 result.push_back(direct_view);
386 result.push_back(encoder->GetLiteralView(l.Negated()));
394 void FilterFalseArcsAtLevelZero(std::vector<int>& tails,
395 std::vector<int>& heads,
396 std::vector<Literal>& literals, Model*
model) {
397 const Trail& trail = *
model->GetOrCreate<Trail>();
398 if (trail.CurrentDecisionLevel() != 0)
return;
401 const int size =
static_cast<int>(tails.size());
402 const VariablesAssignment& assignment = trail.Assignment();
403 for (
int i = 0; i < size; ++i) {
404 if (assignment.LiteralIsFalse(literals[i]))
continue;
405 tails[new_size] = tails[i];
406 heads[new_size] = heads[i];
407 literals[new_size] = literals[i];
410 if (new_size < size) {
411 tails.resize(new_size);
412 heads.resize(new_size);
413 literals.resize(new_size);
423 int num_nodes, std::vector<int> tails, std::vector<int> heads,
424 std::vector<Literal> literals,
Model*
model) {
426 result.
vars = GetAssociatedVariables(literals,
model);
428 [num_nodes, tails, heads, literals,
model](
431 FilterFalseArcsAtLevelZero(tails, heads, literals,
model);
433 num_nodes, tails, heads, literals, lp_values,
434 {}, 0, manager,
model);
441 std::vector<int> heads,
442 std::vector<Literal> literals,
443 std::vector<int64_t> demands,
446 result.
vars = GetAssociatedVariables(literals,
model);
448 [num_nodes, tails, heads, demands,
capacity, literals,
model](
451 FilterFalseArcsAtLevelZero(tails, heads, literals,
model);
453 lp_values, demands,
capacity, manager,
463 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
464 const std::vector<AffineExpression>& arc_capacities,
465 std::function<
void(
const std::vector<bool>& in_subset,
466 IntegerValue* min_incoming_flow,
467 IntegerValue* min_outgoing_flow)>
479 std::vector<Arc> relevant_arcs;
486 std::vector<std::pair<double, int>> arc_by_decreasing_lp_values;
487 for (
int i = 0; i < arc_capacities.size(); ++i) {
488 const double lp_value = arc_capacities[i].LpValue(lp_values);
489 if (!arc_capacities[i].IsConstant()) {
492 if (lp_value < 1e-6 && arc_capacities[i].constant == 0)
continue;
493 relevant_arcs.push_back(
494 {tails[i], heads[i], lp_value, arc_capacities[i].constant});
495 arc_by_decreasing_lp_values.push_back({lp_value, i});
497 if (gcd == 0)
return;
498 std::sort(arc_by_decreasing_lp_values.begin(),
499 arc_by_decreasing_lp_values.end(),
500 std::greater<std::pair<double, int>>());
502 std::vector<std::pair<int, int>> ordered_arcs;
503 for (
const auto& [score,
arc] : arc_by_decreasing_lp_values) {
504 if (tails[
arc] == -1)
continue;
505 if (heads[
arc] == -1)
continue;
506 ordered_arcs.push_back({tails[
arc], heads[
arc]});
508 std::vector<int> subset_data;
509 std::vector<absl::Span<const int>> subsets;
516 std::vector<bool> in_subset(num_nodes,
false);
517 for (
const absl::Span<const int> subset : subsets) {
518 DCHECK(!subset.empty());
519 DCHECK_LE(subset.size(), num_nodes);
522 for (
const int n : subset) in_subset[n] =
true;
524 IntegerValue min_incoming_flow;
525 IntegerValue min_outgoing_flow;
526 get_flows(in_subset, &min_incoming_flow, &min_outgoing_flow);
530 IntegerValue incoming_offset(0);
531 IntegerValue outgoing_offset(0);
538 double lp_outgoing_flow = 0.0;
539 double lp_incoming_flow = 0.0;
540 for (
const auto arc : relevant_arcs) {
541 if (
arc.tail != -1 && in_subset[
arc.tail]) {
542 if (
arc.head == -1 || !in_subset[
arc.head]) {
543 incoming_offset +=
arc.offset;
544 lp_outgoing_flow +=
arc.lp_value;
547 if (
arc.head != -1 && in_subset[
arc.head]) {
548 outgoing_offset +=
arc.offset;
549 lp_incoming_flow +=
arc.lp_value;
560 const IntegerValue test_incoming = min_incoming_flow - incoming_offset;
561 const IntegerValue new_incoming =
562 CeilRatio(test_incoming, IntegerValue(gcd)) * IntegerValue(gcd);
563 const IntegerValue incoming_delta = new_incoming - test_incoming;
564 if (incoming_delta > 0) min_incoming_flow += incoming_delta;
567 const IntegerValue test_outgoing = min_outgoing_flow - outgoing_offset;
568 const IntegerValue new_outgoing =
569 CeilRatio(test_outgoing, IntegerValue(gcd)) * IntegerValue(gcd);
570 const IntegerValue outgoing_delta = new_outgoing - test_outgoing;
571 if (outgoing_delta > 0) min_outgoing_flow += outgoing_delta;
574 if (lp_incoming_flow <
ToDouble(min_incoming_flow) - 1e-6) {
575 VLOG(2) <<
"INCOMING CUT " << lp_incoming_flow
576 <<
" >= " << min_incoming_flow <<
" size " << subset.size()
577 <<
" offset " << incoming_offset <<
" gcd " << gcd;
579 for (
int i = 0; i < tails.size(); ++i) {
580 if ((tails[i] == -1 || !in_subset[tails[i]]) &&
581 (heads[i] != -1 && in_subset[heads[i]])) {
582 cut.
AddTerm(arc_capacities[i], 1.0);
585 manager->
AddCut(cut.
Build(),
"IncomingFlow", lp_values);
588 if (lp_outgoing_flow <
ToDouble(min_outgoing_flow) - 1e-6) {
589 VLOG(2) <<
"OUGOING CUT " << lp_outgoing_flow
590 <<
" >= " << min_outgoing_flow <<
" size " << subset.size()
591 <<
" offset " << outgoing_offset <<
" gcd " << gcd;
593 for (
int i = 0; i < tails.size(); ++i) {
594 if ((tails[i] != -1 && in_subset[tails[i]]) &&
595 (heads[i] == -1 || !in_subset[heads[i]])) {
596 cut.
AddTerm(arc_capacities[i], 1.0);
599 manager->
AddCut(cut.
Build(),
"OutgoingFlow", lp_values);
603 for (
const int n : subset) in_subset[n] =
false;
608 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
609 const std::vector<AffineExpression>& arc_capacities,
610 std::function<
void(
const std::vector<bool>& in_subset,
611 IntegerValue* min_incoming_flow,
612 IntegerValue* min_outgoing_flow)>
617 if (!expr.IsConstant()) result.
vars.push_back(expr.var);
623 get_flows, lp_values, manager,
model);
void push_back(const value_type &x)
static int64_t GCD64(int64_t x, int64_t y)
const IntegerVariable GetLiteralView(Literal lit) const
void AddTerm(IntegerVariable var, IntegerValue coeff)
bool AddCut(const LinearConstraint &ct, std::string type_name, const absl::StrongVector< IntegerVariable, double > &lp_solution, std::string extra_info="")
Class that owns everything related to a particular optimization model.
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
CutGenerator CreateStronglyConnectedGraphCutGenerator(int num_nodes, std::vector< int > tails, std::vector< int > heads, std::vector< Literal > literals, Model *model)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
IntType CeilOfRatio(IntType numerator, IntType denominator)
CutGenerator CreateCVRPCutGenerator(int num_nodes, std::vector< int > tails, std::vector< int > heads, std::vector< Literal > literals, std::vector< int64_t > demands, int64_t capacity, Model *model)
void GenerateInterestingSubsets(int num_nodes, const std::vector< std::pair< int, int >> &arcs, int min_subset_size, int stop_at_num_components, std::vector< int > *subset_data, std::vector< absl::Span< const int >> *subsets)
const IntegerVariable kNoIntegerVariable(-1)
void SeparateFlowInequalities(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< AffineExpression > &arc_capacities, std::function< void(const std::vector< bool > &in_subset, IntegerValue *min_incoming_flow, IntegerValue *min_outgoing_flow)> get_flows, const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraintManager *manager, Model *model)
void SeparateSubtourInequalities(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, const absl::StrongVector< IntegerVariable, double > &lp_values, absl::Span< const int64_t > demands, int64_t capacity, LinearConstraintManager *manager, Model *model)
double ToDouble(IntegerValue value)
CutGenerator CreateFlowCutGenerator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< AffineExpression > &arc_capacities, std::function< void(const std::vector< bool > &in_subset, IntegerValue *min_incoming_flow, IntegerValue *min_outgoing_flow)> get_flows, Model *model)
Collection of objects used to extend the Constraint Solver library.
std::vector< IntegerVariable > vars
std::function< bool(const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraintManager *manager)> generate_cuts
#define VLOG(verboselevel)