Skip to content

Commit 15a12a0

Browse files
committed
[CP-SAT] speed up no_overlap_2d (presolve, propagation); tweak shared tree workers; improve hint preservation during presolve; remove memory contention
1 parent 7501a2b commit 15a12a0

26 files changed

+889
-520
lines changed

ortools/sat/BUILD.bazel

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ cc_library(
196196
"//ortools/port:proto_utils",
197197
"//ortools/util:saturated_arithmetic",
198198
"//ortools/util:sorted_interval_list",
199+
"@com_google_absl//absl/algorithm:container",
199200
"@com_google_absl//absl/container:btree",
200201
"@com_google_absl//absl/container:flat_hash_map",
201202
"@com_google_absl//absl/container:flat_hash_set",
@@ -2926,9 +2927,7 @@ cc_library(
29262927
"//ortools/base",
29272928
"//ortools/base:stl_util",
29282929
"//ortools/base:strong_vector",
2929-
"//ortools/graph",
29302930
"//ortools/graph:connected_components",
2931-
"//ortools/graph:minimum_spanning_tree",
29322931
"//ortools/graph:strongly_connected_components",
29332932
"//ortools/util:fixed_shape_binary_tree",
29342933
"//ortools/util:integer_pq",
@@ -3110,9 +3109,11 @@ cc_test(
31103109
":integer_base",
31113110
":util",
31123111
"//ortools/base",
3113-
"//ortools/base:gmock_main",
3112+
"//ortools/base:fuzztest",
3113+
"//ortools/base:gmock",
31143114
"//ortools/graph:connected_components",
31153115
"//ortools/graph:strongly_connected_components",
3116+
"//ortools/util:saturated_arithmetic",
31163117
"//ortools/util:strong_integers",
31173118
"@com_google_absl//absl/algorithm:container",
31183119
"@com_google_absl//absl/container:flat_hash_map",
@@ -3123,6 +3124,7 @@ cc_test(
31233124
"@com_google_absl//absl/strings",
31243125
"@com_google_absl//absl/types:span",
31253126
"@com_google_benchmark//:benchmark",
3127+
"@com_google_fuzztest//fuzztest:fuzztest_gtest_main",
31263128
],
31273129
)
31283130

ortools/sat/cp_model_checker.cc

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717
#include <cmath>
1818
#include <cstdint>
1919
#include <limits>
20+
#include <optional>
2021
#include <string>
2122
#include <tuple>
2223
#include <utility>
2324
#include <vector>
2425

26+
#include "absl/algorithm/container.h"
2527
#include "absl/container/btree_map.h"
2628
#include "absl/container/flat_hash_map.h"
2729
#include "absl/container/flat_hash_set.h"
@@ -1420,6 +1422,7 @@ class ConstraintChecker {
14201422
const auto& arg = ct.no_overlap_2d();
14211423
// Those intervals from arg.x_intervals and arg.y_intervals where both
14221424
// the x and y intervals are enforced.
1425+
bool has_zero_sizes = false;
14231426
std::vector<Rectangle> enforced_rectangles;
14241427
{
14251428
const int num_intervals = arg.x_intervals_size();
@@ -1432,16 +1435,34 @@ class ConstraintChecker {
14321435
.x_max = IntervalEnd(x.interval()),
14331436
.y_min = IntervalStart(y.interval()),
14341437
.y_max = IntervalEnd(y.interval())});
1438+
const auto& rect = enforced_rectangles.back();
1439+
if (rect.x_min == rect.x_max || rect.y_min == rect.y_max) {
1440+
has_zero_sizes = true;
1441+
}
14351442
}
14361443
}
14371444
}
1438-
const std::vector<std::pair<int, int>> intersections =
1439-
FindPartialRectangleIntersectionsAlsoEmpty(enforced_rectangles);
1440-
if (!intersections.empty()) {
1441-
VLOG(1) << "Rectangles " << intersections[0].first << "("
1442-
<< enforced_rectangles[intersections[0].first] << ") and "
1443-
<< intersections[0].second << "("
1444-
<< enforced_rectangles[intersections[0].second]
1445+
1446+
std::optional<std::pair<int, int>> one_intersection;
1447+
if (!has_zero_sizes) {
1448+
absl::c_stable_sort(enforced_rectangles,
1449+
[](const Rectangle& a, const Rectangle& b) {
1450+
return a.x_min < b.x_min;
1451+
});
1452+
one_intersection = FindOneIntersectionIfPresent(enforced_rectangles);
1453+
} else {
1454+
const std::vector<std::pair<int, int>> intersections =
1455+
FindPartialRectangleIntersections(enforced_rectangles);
1456+
if (!intersections.empty()) {
1457+
one_intersection = intersections[0];
1458+
}
1459+
}
1460+
1461+
if (one_intersection != std::nullopt) {
1462+
VLOG(1) << "Rectangles " << one_intersection->first << "("
1463+
<< enforced_rectangles[one_intersection->first] << ") and "
1464+
<< one_intersection->second << "("
1465+
<< enforced_rectangles[one_intersection->second]
14451466
<< ") are not disjoint.";
14461467
return false;
14471468
}

ortools/sat/cp_model_lns.cc

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,7 @@ NeighborhoodGeneratorHelper::GetActiveRectangles(
471471
}
472472

473473
std::vector<ActiveRectangle> results;
474+
results.reserve(active_rectangles.size());
474475
for (const auto& [rectangle, no_overlap_2d_constraints] : active_rectangles) {
475476
ActiveRectangle& result = results.emplace_back();
476477
result.x_interval = rectangle.first;
@@ -532,7 +533,9 @@ void RestrictAffineExpression(const LinearExpressionProto& expr,
532533
const Domain domain =
533534
ReadDomainFromProto(mutable_proto->variables(expr.vars(0)))
534535
.IntersectionWith(implied_domain);
535-
FillDomainInProto(domain, mutable_proto->mutable_variables(expr.vars(0)));
536+
if (!domain.IsEmpty()) {
537+
FillDomainInProto(domain, mutable_proto->mutable_variables(expr.vars(0)));
538+
}
536539
}
537540

538541
struct StartEndIndex {
@@ -1034,7 +1037,14 @@ std::vector<std::vector<int>> NeighborhoodGeneratorHelper::GetRoutingPaths(
10341037
Neighborhood NeighborhoodGeneratorHelper::FixGivenVariables(
10351038
const CpSolverResponse& base_solution,
10361039
const absl::flat_hash_set<int>& variables_to_fix) const {
1037-
Neighborhood neighborhood;
1040+
int initial_num_variables = 0;
1041+
{
1042+
absl::ReaderMutexLock domain_lock(&domain_mutex_);
1043+
1044+
initial_num_variables =
1045+
model_proto_with_only_variables_->variables().size();
1046+
}
1047+
Neighborhood neighborhood(initial_num_variables);
10381048

10391049
// TODO(user): Maybe relax all variables in the objective when the number
10401050
// is small or negligible compared to the number of variables.

ortools/sat/cp_model_lns.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ namespace sat {
4444

4545
// Neighborhood returned by Neighborhood generators.
4646
struct Neighborhood {
47+
static constexpr int kDefaultArenaSizePerVariable = 128;
48+
49+
explicit Neighborhood(int num_variables_hint = 10)
50+
: arena_buffer(kDefaultArenaSizePerVariable * num_variables_hint),
51+
arena(std::make_unique<google::protobuf::Arena>(arena_buffer.data(),
52+
arena_buffer.size())),
53+
delta(*google::protobuf::Arena::Create<CpModelProto>(arena.get())) {}
54+
4755
// True if neighborhood generator was able to generate a neighborhood.
4856
bool is_generated = false;
4957

@@ -58,7 +66,9 @@ struct Neighborhood {
5866
// The delta will contains all variables from the initial model, potentially
5967
// with updated domains.
6068
// It can contains new variables and new constraints, and solution hinting.
61-
CpModelProto delta;
69+
std::vector<char> arena_buffer;
70+
std::unique_ptr<google::protobuf::Arena> arena;
71+
CpModelProto& delta;
6272

6373
// Neighborhood Id. Used to identify the neighborhood by a generator.
6474
// Currently only used by WeightedRandomRelaxationNeighborhoodGenerator.

0 commit comments

Comments
 (0)