diff --git a/ortools/sat/cp_model_presolve.cc b/ortools/sat/cp_model_presolve.cc index 0fd33c29b9..a8c066acdf 100644 --- a/ortools/sat/cp_model_presolve.cc +++ b/ortools/sat/cp_model_presolve.cc @@ -3027,6 +3027,45 @@ bool CpModelPresolver::PresolveNoOverlap(ConstraintProto* ct) { } } + // Split constraints in disjoint sets. + { + if (proto->intervals_size() > 1) { + std::vector indexed_intervals; + for (int i = 0; i < proto->intervals().size(); ++i) { + const int index = proto->intervals(i); + indexed_intervals.push_back({index, + IntegerValue(context_->StartMin(index)), + IntegerValue(context_->EndMax(index))}); + } + std::vector> components; + ConstructNonOverlappingSets(false, &indexed_intervals, &components); + + if (components.size() > 1) { + NoOverlapConstraintProto* dest = proto; + dest->clear_intervals(); + for (const std::vector& intervals : components) { + if (components.size() <= 1) continue; + + // Create a new no_overlap constraint after the first iteration. + if (dest == nullptr) { + dest = context_->working_model->add_constraints() + ->mutable_no_overlap(); + } + // Fill in the intervals. Unfortunately, the Assign() method does not + // compile in or-tools. + for (const int i : intervals) { + dest->add_intervals(i); + } + // Zero the ptr, so than we can create a new no_overlap constraint + // at the next iteration. + dest = nullptr; + } + changed = true; + context_->UpdateNewConstraintsVariableUsage(); + } + } + } + std::vector constant_intervals; int64_t size_min_of_non_constant_intervals = std::numeric_limits::max(); @@ -3141,38 +3180,6 @@ bool CpModelPresolver::PresolveNoOverlap(ConstraintProto* ct) { } } - if (proto->intervals_size() > 1) { - // Sort all intervals by start min. - std::sort(proto->mutable_intervals()->begin(), - proto->mutable_intervals()->end(), [this](int i1, int i2) { - return context_->StartMin(i1) < context_->StartMin(i2); - }); - - // Split no_overlap in disjoint subsets. - int64_t end_max_so_far = context_->EndMax(proto->intervals(0)); - for (int i = 1; i < proto->intervals_size(); ++i) { - const int interval_index = proto->intervals(i); - if (context_->StartMin(interval_index) >= end_max_so_far) { - // Create a new overlap with the rest. - // TODO(user): We can split all at once. - // TODO(user,user): Can we split more aggressively ? - NoOverlapConstraintProto* new_ct = - context_->working_model->add_constraints()->mutable_no_overlap(); - for (int j = i; j < proto->intervals_size(); ++j) { - new_ct->add_intervals(ct->no_overlap().intervals(j)); - } - proto->mutable_intervals()->Truncate(i); - context_->UpdateNewConstraintsVariableUsage(); - context_->UpdateRuleStats( - "no_overlap: split into disjoint no_overlap constraints"); - changed = true; - break; - } - end_max_so_far = - std::max(end_max_so_far, context_->EndMax(interval_index)); - } - } - if (proto->intervals_size() == 1) { context_->UpdateRuleStats("no_overlap: only one interval"); return RemoveConstraint(ct); @@ -3243,6 +3250,8 @@ bool CpModelPresolver::PresolveNoOverlap2D(int c, ConstraintProto* ct) { &no_overlaps); for (const std::vector& no_overlap : no_overlaps) { ConstraintProto* new_ct = context_->working_model->add_constraints(); + // Unfortunately, the Assign() method does not work in or-tools as the + // protobuf int32_t type is not the int type. for (const int i : no_overlap) { new_ct->mutable_no_overlap()->add_intervals(i); } diff --git a/ortools/sat/diffn_util.cc b/ortools/sat/diffn_util.cc index 13e5849035..c859e82e6b 100644 --- a/ortools/sat/diffn_util.cc +++ b/ortools/sat/diffn_util.cc @@ -389,5 +389,41 @@ void ConstructOverlappingSets(bool already_sorted, } } +void ConstructNonOverlappingSets(bool already_sorted, + std::vector* intervals, + std::vector>* result) { + result->clear(); + if (intervals->empty()) return; + if (intervals->size() == 1) { + result->push_back({intervals->front().index}); + return; + } + + if (already_sorted) { + DCHECK( + std::is_sorted(intervals->begin(), intervals->end(), + [](const IndexedInterval& a, const IndexedInterval& b) { + return a.start < b.start; + })); + } else { + std::sort(intervals->begin(), intervals->end(), + [](const IndexedInterval& a, const IndexedInterval& b) { + return a.start < b.start; + }); + } + + IntegerValue end_max_so_far = (*intervals)[0].end; + result->push_back({(*intervals)[0].index}); + for (int i = 1; i < intervals->size(); ++i) { + const IndexedInterval& j = (*intervals)[i]; + if (j.start >= end_max_so_far) { + result->push_back({j.index}); + } else { + result->back().push_back(j.index); + } + end_max_so_far = std::max(end_max_so_far, j.end); + } +} + } // namespace sat } // namespace operations_research diff --git a/ortools/sat/diffn_util.h b/ortools/sat/diffn_util.h index a229776ba9..943014e27e 100644 --- a/ortools/sat/diffn_util.h +++ b/ortools/sat/diffn_util.h @@ -120,6 +120,12 @@ void ConstructOverlappingSets(bool already_sorted, std::vector* intervals, std::vector>* result); +// Given n intervals, returns the set of connected components (using the overlap +// relation between 2 intervals). +void ConstructNonOverlappingSets(bool already_sorted, + std::vector* intervals, + std::vector>* result); + } // namespace sat } // namespace operations_research