Skip to content

Commit

Permalink
[CP-SAT] more work on 2d packing
Browse files Browse the repository at this point in the history
  • Loading branch information
lperron committed Dec 5, 2024
1 parent d8c3e49 commit 1369b4c
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 32 deletions.
8 changes: 8 additions & 0 deletions ortools/sat/2d_rectangle_presolve.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<Rectangle> empty_vec;
if (ReduceNumberofBoxesGreedy(fixed_boxes, &empty_vec)) {
changed = true;
}

IntegerValue min_x_size = std::numeric_limits<IntegerValue>::max();
IntegerValue min_y_size = std::numeric_limits<IntegerValue>::max();

Expand Down
15 changes: 15 additions & 0 deletions ortools/sat/cp_model_presolve.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
59 changes: 29 additions & 30 deletions ortools/sat/diffn_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2015,38 +2015,36 @@ absl::optional<std::pair<int, int>> 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> interval_set;
std::set<Element> 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()) {
Expand All @@ -2057,8 +2055,9 @@ absl::optional<std::pair<int, int>> 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}};
}
Expand All @@ -2073,8 +2072,8 @@ absl::optional<std::pair<int, int>> 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}};
}
Expand Down
2 changes: 1 addition & 1 deletion ortools/sat/routing_cuts.cc
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ void ExtractAllSubsetsFromForest(absl::Span<const int> parent,
}

std::vector<int> ComputeGomoryHuTree(
int num_nodes, const std::vector<ArcWithLpValue>& relevant_arcs) {
int num_nodes, absl::Span<const ArcWithLpValue> 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;
Expand Down
2 changes: 1 addition & 1 deletion ortools/sat/routing_cuts.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ void SymmetrizeArcs(std::vector<ArcWithLpValue>* arcs);
// Pairs Network Flow Analysis", Dan Gusfield, 1990,
// https://ranger.uta.edu/~weems/NOTES5311/LAB/LAB2SPR21/gusfield.huGomory.pdf
std::vector<int> ComputeGomoryHuTree(
int num_nodes, const std::vector<ArcWithLpValue>& relevant_arcs);
int num_nodes, absl::Span<const ArcWithLpValue> 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
Expand Down

0 comments on commit 1369b4c

Please sign in to comment.