Skip to content

Commit b3a9359

Browse files
committed
[CP-SAT] fix python layer when an exception is thrown in a callback; bug fixes
1 parent 18b8cb9 commit b3a9359

19 files changed

+982
-166
lines changed

ortools/sat/2d_packing_brute_force.cc

Lines changed: 41 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,18 @@
1414
#include "ortools/sat/2d_packing_brute_force.h"
1515

1616
#include <algorithm>
17-
#include <array>
1817
#include <limits>
1918
#include <tuple>
2019
#include <utility>
2120
#include <vector>
2221

23-
#include "absl/container/inlined_vector.h"
2422
#include "absl/log/check.h"
2523
#include "absl/strings/str_cat.h"
2624
#include "absl/types/span.h"
2725
#include "ortools/base/logging.h"
2826
#include "ortools/sat/diffn_util.h"
2927
#include "ortools/sat/integer_base.h"
28+
#include "ortools/sat/util.h"
3029
#include "ortools/util/bitset.h"
3130

3231
namespace operations_research {
@@ -143,25 +142,28 @@ bool BruteForceOrthogonalPackingImpl(
143142
std::pair<IntegerValue, IntegerValue> bounding_box_size,
144143
IntegerValue smallest_x, IntegerValue smallest_y,
145144
absl::Span<Rectangle> item_positions, Bitset64<int>& placed_item_indexes,
146-
absl::Span<const absl::InlinedVector<PotentialPositionForItem, 16>>
145+
const CompactVectorVector<int, PotentialPositionForItem>&
147146
potential_item_positions,
148147
IntegerValue slack) {
149148
const auto add_position_if_valid =
150149
[&item_positions, bounding_box_size, &sizes_x, &sizes_y,
151150
&placed_item_indexes](
152-
absl::InlinedVector<PotentialPositionForItem, 16>& positions, int i,
151+
CompactVectorVector<int, PotentialPositionForItem>& positions, int i,
153152
IntegerValue x, IntegerValue y) {
154153
const Rectangle rect = {.x_min = x,
155154
.x_max = x + sizes_x[i],
156155
.y_min = y,
157156
.y_max = y + sizes_y[i]};
158157
if (ShouldPlaceItemAtPosition(i, rect, bounding_box_size,
159158
item_positions, placed_item_indexes)) {
160-
positions.push_back({x, y, false});
159+
positions.AppendToLastVector({x, y, false});
161160
}
162161
};
163162

164163
const int num_items = sizes_x.size();
164+
CompactVectorVector<int, PotentialPositionForItem> new_potential_positions;
165+
new_potential_positions.reserve(num_items,
166+
3 * potential_item_positions.num_entries());
165167
bool has_unplaced_item = false;
166168
for (int i = 0; i < num_items; ++i) {
167169
if (placed_item_indexes[i]) {
@@ -261,48 +263,31 @@ bool BruteForceOrthogonalPackingImpl(
261263
// (like the point in the left boundary in the example above) but we will
262264
// detect that by testing each item one by one. Importantly, we only pass
263265
// valid positions down to the next search level.
264-
std::array<absl::InlinedVector<PotentialPositionForItem, 16>,
265-
kMaxProblemSize>
266-
new_potential_positions_storage;
267-
absl::Span<absl::InlinedVector<PotentialPositionForItem, 16>>
268-
new_potential_positions(new_potential_positions_storage.data(),
269-
num_items);
270-
for (const int k : placed_item_indexes) {
271-
if (k == i) {
266+
new_potential_positions.clear();
267+
bool is_unfeasible = false;
268+
for (int j = 0; j < num_items; ++j) {
269+
// We will find all potential positions for the j-th item.
270+
new_potential_positions.Add({});
271+
// No need to attribute potential positions to already placed items
272+
// (including the one we just placed).
273+
if (placed_item_indexes[j]) {
272274
continue;
273275
}
274-
275-
const bool add_below =
276-
// We only add points below this one...
277-
item_positions[k].y_max <= item_position.y_max &&
278-
// ...and where we can fit at least the smallest element.
279-
item_position.x_max + smallest_x <= bounding_box_size.first &&
280-
item_positions[k].y_max + smallest_y <= bounding_box_size.second;
281-
const bool add_left =
282-
item_positions[k].x_max <= item_position.x_max &&
283-
item_positions[k].x_max + smallest_x <= bounding_box_size.first &&
284-
item_position.y_max + smallest_y <= bounding_box_size.second;
285-
for (int j = 0; j < num_items; ++j) {
286-
if (k == j || placed_item_indexes[j]) {
276+
DCHECK_NE(i, j);
277+
// Add the new "potential positions" derived from the new item.
278+
for (const int k : placed_item_indexes) {
279+
DCHECK_NE(j, k);
280+
if (k == i) {
287281
continue;
288282
}
289-
if (add_below) {
290-
add_position_if_valid(new_potential_positions[j], j,
291-
item_position.x_max, item_positions[k].y_max);
292-
}
293-
if (add_left) {
294-
add_position_if_valid(new_potential_positions[j], j,
295-
item_positions[k].x_max, item_position.y_max);
296-
}
297-
}
298-
}
299-
bool is_unfeasible = false;
300-
for (int j = 0; j < num_items; ++j) {
301-
// No positions to attribute to the item we just placed.
302-
if (i == j || placed_item_indexes[j]) {
303-
continue;
283+
284+
add_position_if_valid(new_potential_positions, j, item_position.x_max,
285+
item_positions[k].y_max);
286+
add_position_if_valid(new_potential_positions, j,
287+
item_positions[k].x_max, item_position.y_max);
304288
}
305-
// First copy previously valid positions that remain valid.
289+
290+
// Then copy previously valid positions that remain valid.
306291
for (const PotentialPositionForItem& original_position :
307292
potential_item_positions[j]) {
308293
if (!original_position.GetRectangle(sizes_x[j], sizes_y[j])
@@ -319,14 +304,14 @@ bool BruteForceOrthogonalPackingImpl(
319304
// have to retest those positions down in the search tree.
320305
PotentialPositionForItem position = original_position;
321306
position.already_explored = true;
322-
new_potential_positions[j].push_back(position);
307+
new_potential_positions.AppendToLastVector(position);
323308
} else {
324-
new_potential_positions[j].push_back(original_position);
309+
new_potential_positions.AppendToLastVector(original_position);
325310
}
326311
}
327-
add_position_if_valid(new_potential_positions[j], j,
312+
add_position_if_valid(new_potential_positions, j,
328313
item_positions[i].x_max, 0);
329-
add_position_if_valid(new_potential_positions[j], j, 0,
314+
add_position_if_valid(new_potential_positions, j, 0,
330315
item_positions[i].y_max);
331316
if (new_potential_positions[j].empty()) {
332317
// After placing the item i, there is no valid place to choose for the
@@ -380,27 +365,21 @@ bool BruteForceOrthogonalPackingNoPreprocessing(
380365
std::make_tuple(b.size_x * b.size_y, b.index);
381366
});
382367

383-
std::array<IntegerValue, kMaxProblemSize> new_sizes_x_storage,
384-
new_sizes_y_storage;
385-
absl::Span<IntegerValue> new_sizes_x(new_sizes_x_storage.data(), num_items);
386-
absl::Span<IntegerValue> new_sizes_y(new_sizes_y_storage.data(), num_items);
387-
std::array<absl::InlinedVector<PotentialPositionForItem, 16>, kMaxProblemSize>
388-
potential_item_positions_storage;
389-
absl::Span<absl::InlinedVector<PotentialPositionForItem, 16>>
390-
potential_item_positions(potential_item_positions_storage.data(),
391-
num_items);
368+
std::vector<IntegerValue> new_sizes_x(num_items);
369+
std::vector<IntegerValue> new_sizes_y(num_items);
370+
CompactVectorVector<int, PotentialPositionForItem> potential_item_positions;
371+
potential_item_positions.reserve(num_items, num_items);
392372
for (int i = 0; i < num_items; ++i) {
393373
new_sizes_x[i] = items[i].size_x;
394374
new_sizes_y[i] = items[i].size_y;
395-
potential_item_positions[i].push_back({0, 0, false});
375+
potential_item_positions.Add({PotentialPositionForItem{0, 0, false}});
396376
}
397-
std::array<Rectangle, kMaxProblemSize> item_positions_storage;
398-
absl::Span<Rectangle> item_positions(item_positions_storage.data(),
399-
num_items);
377+
std::vector<Rectangle> item_positions(num_items);
400378
Bitset64<int> placed_item_indexes(num_items);
401379
const bool found_solution = BruteForceOrthogonalPackingImpl(
402380
new_sizes_x, new_sizes_y, bounding_box_size, smallest_x, smallest_y,
403-
item_positions, placed_item_indexes, potential_item_positions, slack);
381+
absl::MakeSpan(item_positions), placed_item_indexes,
382+
potential_item_positions, slack);
404383
if (!found_solution) {
405384
return false;
406385
}
@@ -651,13 +630,12 @@ BruteForceResult BruteForceOrthogonalPacking(
651630
}
652631
CHECK_LE(num_items, kMaxProblemSize);
653632

654-
std::array<PermutableItem, kMaxProblemSize> items_storage;
655-
absl::Span<PermutableItem> items(items_storage.data(), num_items);
633+
std::vector<PermutableItem> items(num_items);
656634
for (int i = 0; i < num_items; ++i) {
657635
items[i] = {
658636
.size_x = sizes_x[i], .size_y = sizes_y[i], .index = i, .position = {}};
659637
}
660-
absl::Span<PermutableItem> post_processed_items = items;
638+
absl::Span<PermutableItem> post_processed_items = absl::MakeSpan(items);
661639
std::pair<IntegerValue, IntegerValue> post_processed_bounding_box_size =
662640
bounding_box_size;
663641
const bool post_processed =

ortools/sat/2d_rectangle_presolve.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <vector>
2424

2525
#include "absl/algorithm/container.h"
26+
#include "absl/base/log_severity.h"
2627
#include "absl/container/btree_map.h"
2728
#include "absl/container/flat_hash_map.h"
2829
#include "absl/container/flat_hash_set.h"
@@ -47,6 +48,7 @@ bool PresolveFixed2dRectangles(
4748
// `fixed_boxes`.
4849
bool changed = false;
4950

51+
DCHECK(FindPartialRectangleIntersections(*fixed_boxes).empty());
5052
IntegerValue original_area = 0;
5153
std::vector<Rectangle> fixed_boxes_copy;
5254
if (VLOG_IS_ON(1)) {
@@ -78,13 +80,17 @@ bool PresolveFixed2dRectangles(
7880
min_x_size = std::min(min_x_size, box.x_size);
7981
min_y_size = std::min(min_y_size, box.y_size);
8082
}
83+
DCHECK_GT(min_x_size, 0);
84+
DCHECK_GT(min_y_size, 0);
8185

8286
// Fixed items are only useful to constraint where the non-fixed items can be
8387
// placed. This means in particular that any part of a fixed item outside the
8488
// bounding box of the non-fixed items is useless. Clip them.
8589
int new_size = 0;
8690
while (new_size < fixed_boxes->size()) {
8791
Rectangle& rectangle = (*fixed_boxes)[new_size];
92+
DCHECK_GT(rectangle.SizeX(), 0);
93+
DCHECK_GT(rectangle.SizeY(), 0);
8894
if (rectangle.x_min < bounding_box.x_min) {
8995
rectangle.x_min = bounding_box.x_min;
9096
changed = true;
@@ -1438,13 +1444,20 @@ bool ReduceNumberOfBoxesExactMandatory(
14381444
const Neighbours neighbours = BuildNeighboursGraph(result);
14391445
std::vector<SingleShape> shapes = BoxesToShapes(result, neighbours);
14401446

1447+
std::vector<Rectangle> original_result;
1448+
if (DEBUG_MODE) {
1449+
original_result = result;
1450+
}
14411451
result.clear();
14421452
for (SingleShape& shape : shapes) {
14431453
// This is the function that applies the algorithm described in [1].
14441454
const std::vector<Rectangle> cut_rectangles =
14451455
CutShapeIntoRectangles(std::move(shape));
14461456
result.insert(result.end(), cut_rectangles.begin(), cut_rectangles.end());
14471457
}
1458+
DCHECK(RegionIncludesOther(original_result, result) &&
1459+
RegionIncludesOther(result, original_result));
1460+
14481461
// It is possible that the algorithm actually increases the number of boxes.
14491462
// See the "Problematic2" test.
14501463
if (result.size() >= mandatory_rectangles->size()) return false;

ortools/sat/BUILD.bazel

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,6 @@ cc_library(
776776
"//ortools/util:sorted_interval_list",
777777
"//ortools/util:time_limit",
778778
"@com_google_absl//absl/base:core_headers",
779-
"@com_google_absl//absl/base:log_severity",
780779
"@com_google_absl//absl/container:btree",
781780
"@com_google_absl//absl/container:flat_hash_map",
782781
"@com_google_absl//absl/container:flat_hash_set",
@@ -897,6 +896,7 @@ cc_library(
897896
"@com_google_absl//absl/container:btree",
898897
"@com_google_absl//absl/container:flat_hash_map",
899898
"@com_google_absl//absl/container:flat_hash_set",
899+
"@com_google_absl//absl/flags:flag",
900900
"@com_google_absl//absl/hash",
901901
"@com_google_absl//absl/log",
902902
"@com_google_absl//absl/log:check",
@@ -1714,11 +1714,17 @@ cc_library(
17141714
srcs = ["no_overlap_2d_helper.cc"],
17151715
hdrs = ["no_overlap_2d_helper.h"],
17161716
deps = [
1717+
":2d_rectangle_presolve",
17171718
":diffn_util",
17181719
":integer",
17191720
":integer_base",
17201721
":model",
1722+
":sat_base",
17211723
":scheduling_helpers",
1724+
":util",
1725+
"@com_google_absl//absl/algorithm:container",
1726+
"@com_google_absl//absl/log",
1727+
"@com_google_absl//absl/strings",
17221728
"@com_google_absl//absl/types:span",
17231729
],
17241730
)
@@ -3036,6 +3042,7 @@ cc_library(
30363042
deps = [
30373043
":diffn_util",
30383044
":integer_base",
3045+
":util",
30393046
"//ortools/util:bitset",
30403047
"@com_google_absl//absl/container:inlined_vector",
30413048
"@com_google_absl//absl/log",
@@ -3075,6 +3082,7 @@ cc_library(
30753082
"//ortools/graph:minimum_vertex_cover",
30763083
"//ortools/graph:strongly_connected_components",
30773084
"@com_google_absl//absl/algorithm:container",
3085+
"@com_google_absl//absl/base:log_severity",
30783086
"@com_google_absl//absl/container:btree",
30793087
"@com_google_absl//absl/container:flat_hash_map",
30803088
"@com_google_absl//absl/container:flat_hash_set",
@@ -3235,6 +3243,7 @@ cc_test(
32353243
":cp_model_cc_proto",
32363244
":cp_model_solver",
32373245
":diffn",
3246+
":diffn_util",
32383247
":integer",
32393248
":integer_base",
32403249
":integer_search",

0 commit comments

Comments
 (0)