From 1369b4c3b99976c1dbd6e516863c4be62f7b4b46 Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Thu, 5 Dec 2024 13:42:37 +0100 Subject: [PATCH] [CP-SAT] more work on 2d packing --- ortools/sat/2d_rectangle_presolve.cc | 8 ++++ ortools/sat/cp_model_presolve.cc | 15 +++++++ ortools/sat/diffn_util.cc | 59 ++++++++++++++-------------- ortools/sat/routing_cuts.cc | 2 +- ortools/sat/routing_cuts.h | 2 +- 5 files changed, 54 insertions(+), 32 deletions(-) diff --git a/ortools/sat/2d_rectangle_presolve.cc b/ortools/sat/2d_rectangle_presolve.cc index db187f5fed..a3f8157049 100644 --- a/ortools/sat/2d_rectangle_presolve.cc +++ b/ortools/sat/2d_rectangle_presolve.cc @@ -59,6 +59,14 @@ bool PresolveFixed2dRectangles( } const int original_num_boxes = fixed_boxes->size(); + + // The greedy algorithm is really fast. Run it first since it might greatly + // reduce the size of large trivial instances. + std::vector empty_vec; + if (ReduceNumberofBoxesGreedy(fixed_boxes, &empty_vec)) { + changed = true; + } + IntegerValue min_x_size = std::numeric_limits::max(); IntegerValue min_y_size = std::numeric_limits::max(); diff --git a/ortools/sat/cp_model_presolve.cc b/ortools/sat/cp_model_presolve.cc index 286439f0b3..cd190f9a2d 100644 --- a/ortools/sat/cp_model_presolve.cc +++ b/ortools/sat/cp_model_presolve.cc @@ -3883,6 +3883,21 @@ bool CpModelPresolver::PropagateDomainsInLinear(int ct_index, } if (fixed) { context_->UpdateRuleStats("linear: tightened into equality"); + // Compute a new `var` hint so that the lhs of `ct` is equal to `rhs`. + int64_t var_hint = rhs.FixedValue(); + bool var_hint_is_valid = true; + for (int j = 0; j < num_vars; ++j) { + if (j == i) continue; + const int term_var = ct->linear().vars(j); + if (!context_->VarHasSolutionHint(term_var)) { + var_hint_is_valid = false; + break; + } + var_hint -= context_->SolutionHint(term_var) * ct->linear().coeffs(j); + } + if (var_hint_is_valid) { + context_->UpdateRefSolutionHint(var, var_hint / var_coeff); + } FillDomainInProto(rhs, ct->mutable_linear()); negated_rhs = rhs.Negation(); diff --git a/ortools/sat/diffn_util.cc b/ortools/sat/diffn_util.cc index fe14274e89..aca22b58c8 100644 --- a/ortools/sat/diffn_util.cc +++ b/ortools/sat/diffn_util.cc @@ -2015,38 +2015,36 @@ absl::optional> FindOneIntersectionIfPresent( return a.x_min < b.x_min; })); - // Current y-coordinate intervals that are intersecting the sweep line. - // Note that the interval_set only contains disjoint intervals. - struct Interval { - int index; + // Set of box intersection the sweep line. We only store y_min, other + // coordinates can be accessed via rectangles[index].coordinate. + struct Element { + mutable int index; IntegerValue y_min; - IntegerValue y_max; - - // IMPORTANT: For correctness, we need later insert to be first! - bool operator<(const Interval& other) const { - if (y_min == other.y_min) return index > other.index; - return y_min < other.y_min; - } - - std::string to_string() const { - return absl::StrCat("[", y_min.value(), ",", y_max.value(), "](", index, - ")"); - } + bool operator<(const Element& other) const { return y_min < other.y_min; } }; - - // TODO(user): Use fixed binary tree instead, it should be faster. - // We just need insert/erase/previous/next API. - std::set interval_set; + std::set interval_set; for (int i = 0; i < rectangles.size(); ++i) { const IntegerValue x = rectangles[i].x_min; + const IntegerValue y_min = rectangles[i].y_min; + const IntegerValue y_max = rectangles[i].y_max; + + // TODO(user): We can handle that, but it require some changes below. + DCHECK_LE(y_min, y_max); - // Try to add the y part of this rectangle to the set, if there is an - // intersection, lazily remove it if its x_max is already passed, otherwise - // report the intersection. - const Interval to_insert = {i, rectangles[i].y_min, rectangles[i].y_max}; - auto [it, inserted] = interval_set.insert(to_insert); - DCHECK(inserted); + // Try to add this rectangle to the set, if there is an intersection, lazily + // remove it if its x_max is already passed, otherwise report the + // intersection. + auto [it, inserted] = interval_set.insert({i, y_min}); + if (!inserted) { + if (rectangles[it->index].x_max <= x) { + // We just replace if the rectangle at position i is stale. + it->index = i; + } else { + // Intersection. + return {{it->index, i}}; + } + } // Note that the intersection is either before 'it', or just after it. if (it != interval_set.begin()) { @@ -2057,8 +2055,9 @@ absl::optional> FindOneIntersectionIfPresent( if (rectangles[it_before->index].x_max <= x) { interval_set.erase(it_before); } else { - DCHECK_LE(it_before->y_min, to_insert.y_min); - if (it_before->y_max > to_insert.y_min) { + DCHECK_LE(it_before->y_min, y_min); + const IntegerValue y_max_before = rectangles[it_before->index].y_max; + if (y_max_before > y_min) { // Intersection. return {{it_before->index, i}}; } @@ -2073,8 +2072,8 @@ absl::optional> FindOneIntersectionIfPresent( continue; } - DCHECK_LE(to_insert.y_min, it->y_min); - if (to_insert.y_max > it->y_min) { + DCHECK_LE(y_min, it->y_min); + if (y_max > it->y_min) { // Intersection. return {{it->index, i}}; } diff --git a/ortools/sat/routing_cuts.cc b/ortools/sat/routing_cuts.cc index 5469788776..aa6fa9559f 100644 --- a/ortools/sat/routing_cuts.cc +++ b/ortools/sat/routing_cuts.cc @@ -516,7 +516,7 @@ void ExtractAllSubsetsFromForest(absl::Span parent, } std::vector ComputeGomoryHuTree( - int num_nodes, const std::vector& relevant_arcs) { + int num_nodes, absl::Span relevant_arcs) { // Initialize the graph. Note that we use only arcs with a relevant lp // value, so this should be small in practice. SimpleMaxFlow max_flow; diff --git a/ortools/sat/routing_cuts.h b/ortools/sat/routing_cuts.h index 5df9b09179..6dad882654 100644 --- a/ortools/sat/routing_cuts.h +++ b/ortools/sat/routing_cuts.h @@ -108,7 +108,7 @@ void SymmetrizeArcs(std::vector* arcs); // Pairs Network Flow Analysis", Dan Gusfield, 1990, // https://ranger.uta.edu/~weems/NOTES5311/LAB/LAB2SPR21/gusfield.huGomory.pdf std::vector ComputeGomoryHuTree( - int num_nodes, const std::vector& relevant_arcs); + int num_nodes, absl::Span relevant_arcs); // Cut generator for the circuit constraint, where in any feasible solution, the // arcs that are present (variable at 1) must form a circuit through all the