Skip to content

Commit 3a73fa1

Browse files
committed
[CP-SAT] fix cumulative cuts
1 parent e339013 commit 3a73fa1

File tree

9 files changed

+161
-220
lines changed

9 files changed

+161
-220
lines changed

ortools/sat/diffn_cuts.cc

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,12 @@ CutGenerator CreateNoOverlap2dEnergyCutGenerator(
314314
NoOverlap2DConstraintHelper* helper, Model* model) {
315315
CutGenerator result;
316316
result.only_run_at_level_zero = true;
317-
AddIntegerVariableFromIntervals(&helper->x_helper(), model, &result.vars);
318-
AddIntegerVariableFromIntervals(&helper->y_helper(), model, &result.vars);
317+
AddIntegerVariableFromIntervals(
318+
&helper->x_helper(), model, &result.vars,
319+
IntegerVariablesToAddMask::kSize | IntegerVariablesToAddMask::kPresence);
320+
AddIntegerVariableFromIntervals(
321+
&helper->y_helper(), model, &result.vars,
322+
IntegerVariablesToAddMask::kSize | IntegerVariablesToAddMask::kPresence);
319323
gtl::STLSortAndRemoveDuplicates(&result.vars);
320324
ProductDecomposer* product_decomposer =
321325
model->GetOrCreate<ProductDecomposer>();
@@ -558,8 +562,12 @@ CutGenerator CreateNoOverlap2dCompletionTimeCutGenerator(
558562
NoOverlap2DConstraintHelper* helper, Model* model) {
559563
CutGenerator result;
560564
result.only_run_at_level_zero = true;
561-
AddIntegerVariableFromIntervals(&helper->x_helper(), model, &result.vars);
562-
AddIntegerVariableFromIntervals(&helper->y_helper(), model, &result.vars);
565+
AddIntegerVariableFromIntervals(
566+
&helper->x_helper(), model, &result.vars,
567+
IntegerVariablesToAddMask::kEnd | IntegerVariablesToAddMask::kSize);
568+
AddIntegerVariableFromIntervals(
569+
&helper->y_helper(), model, &result.vars,
570+
IntegerVariablesToAddMask::kEnd | IntegerVariablesToAddMask::kSize);
563571
gtl::STLSortAndRemoveDuplicates(&result.vars);
564572

565573
auto* product_decomposer = model->GetOrCreate<ProductDecomposer>();

ortools/sat/integer_base.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ inline bool AddProductTo(IntegerValue a, IntegerValue b, IntegerValue* result) {
140140
return true;
141141
}
142142

143+
// Computes result += a * a, and return false iff there is an overflow.
144+
inline bool AddSquareTo(IntegerValue a, IntegerValue* result) {
145+
return AddProductTo(a, a, result);
146+
}
147+
143148
// Index of an IntegerVariable.
144149
//
145150
// Each time we create an IntegerVariable we also create its negation. This is

ortools/sat/integer_expr.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,9 +456,10 @@ inline std::function<void(Model*)> WeightedSumGreaterOrEqual(
456456
// Weighted sum == constant.
457457
template <typename VectorInt>
458458
inline std::function<void(Model*)> FixedWeightedSum(
459-
const std::vector<IntegerVariable>& vars, const VectorInt& coefficients,
459+
absl::Span<const IntegerVariable> vars, const VectorInt& coefficients,
460460
int64_t value) {
461-
return [=](Model* model) {
461+
return [=, vars = std::vector<IntegerVariable>(vars.begin(), vars.end())](
462+
Model* model) {
462463
model->Add(WeightedSumGreaterOrEqual(vars, coefficients, value));
463464
model->Add(WeightedSumLowerOrEqual(vars, coefficients, value));
464465
};

ortools/sat/linear_relaxation.cc

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,10 +1070,13 @@ void AppendNoOverlap2dRelaxation(const ConstraintProto& ct, Model* model,
10701070

10711071
CutGenerator& result = relaxation->cut_generators.emplace_back();
10721072
result.only_run_at_level_zero = true;
1073-
AddIntegerVariableFromIntervals(&no_overlap_helper->x_helper(), model,
1074-
&result.vars);
1075-
AddIntegerVariableFromIntervals(&no_overlap_helper->y_helper(), model,
1076-
&result.vars);
1073+
1074+
AddIntegerVariableFromIntervals(
1075+
&no_overlap_helper->x_helper(), model, &result.vars,
1076+
IntegerVariablesToAddMask::kSize | IntegerVariablesToAddMask::kPresence);
1077+
AddIntegerVariableFromIntervals(
1078+
&no_overlap_helper->y_helper(), model, &result.vars,
1079+
IntegerVariablesToAddMask::kSize | IntegerVariablesToAddMask::kPresence);
10771080
result.generate_cuts = [no_overlap_helper, model, product_decomposer,
10781081
last_level_zero_bound_change_idx = int64_t{-1}](
10791082
LinearConstraintManager* manager) mutable {

ortools/sat/routing_cuts.cc

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
namespace operations_research {
4848
namespace sat {
4949

50+
namespace {
51+
5052
// Helper to compute lower bounds based on binary relations enforced by arc
5153
// literals in a RoutesConstraint.
5254
class LowerBoundsHelper {
@@ -171,6 +173,8 @@ class LowerBoundsHelper {
171173
lower_bound_by_var_and_arc_index_;
172174
};
173175

176+
} // namespace
177+
174178
MinOutgoingFlowHelper::MinOutgoingFlowHelper(
175179
int num_nodes, const std::vector<int>& tails, const std::vector<int>& heads,
176180
const std::vector<Literal>& literals, Model* model)
@@ -310,6 +314,7 @@ class OutgoingCutHelper {
310314
// Compute the total demands in order to know the minimum incoming/outgoing
311315
// flow.
312316
for (const int64_t demand : demands) total_demand_ += demand;
317+
complement_of_subset_.reserve(num_nodes_);
313318
}
314319

315320
// Try to add an outgoing cut from the given subset.
@@ -356,6 +361,7 @@ class OutgoingCutHelper {
356361

357362
int64_t total_demand_ = 0;
358363
std::vector<bool> in_subset_;
364+
std::vector<int> complement_of_subset_;
359365
MinOutgoingFlowHelper min_outgoing_flow_helper_;
360366
};
361367

@@ -469,15 +475,23 @@ bool OutgoingCutHelper::TrySubsetCut(std::string name,
469475
// Bounds inferred automatically from the enforced binary relation of the
470476
// model.
471477
//
472-
// TODO(user): use the complement of subset if contain_depot?
473-
//
474478
// TODO(user): This is still not as good as the "capacity" bounds below in
475479
// some cases. Fix! we should be able to use the same relation to infer the
476480
// capacity bounds somehow.
477-
if (subset.size() <= routing_cut_subset_size_for_binary_relation_bound_ &&
478-
!contain_depot) {
479-
const int64_t automatic_bound =
480-
min_outgoing_flow_helper_.ComputeMinOutgoingFlow(subset);
481+
if ((contain_depot ? num_nodes_ - subset.size() : subset.size()) <=
482+
routing_cut_subset_size_for_binary_relation_bound_) {
483+
int automatic_bound;
484+
if (contain_depot) {
485+
complement_of_subset_.clear();
486+
for (int i = 0; i < num_nodes_; ++i) {
487+
if (!in_subset_[i]) complement_of_subset_.push_back(i);
488+
}
489+
automatic_bound = min_outgoing_flow_helper_.ComputeMinOutgoingFlow(
490+
complement_of_subset_);
491+
} else {
492+
automatic_bound =
493+
min_outgoing_flow_helper_.ComputeMinOutgoingFlow(subset);
494+
}
481495
if (automatic_bound > min_outgoing_flow) {
482496
absl::StrAppend(&name, "Automatic");
483497
min_outgoing_flow = automatic_bound;

ortools/sat/sat_parameters.proto

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -914,7 +914,9 @@ message SatParameters {
914914
// If the size of a subset of nodes of a RoutesConstraint is less than this
915915
// value, use linear constraints of size 1 and 2 (such as capacity and time
916916
// window constraints) enforced by the arc literals to compute cuts for this
917-
// subset.
917+
// subset. The algorithm for these cuts has a O(n^3) complexity, where n is
918+
// the subset size. Hence the value of this parameter should not be too large
919+
// (e.g. 10 or 20).
918920
optional int32 routing_cut_subset_size_for_binary_relation_bound = 312
919921
[default = 0];
920922

0 commit comments

Comments
 (0)