Skip to content

Commit

Permalink
[CP-SAT] speed up no_overlap_2d (presolve, propagation); tweak shared…
Browse files Browse the repository at this point in the history
… tree workers; improve hint preservation during presolve; remove memory contention
  • Loading branch information
lperron committed Dec 13, 2024
1 parent 4f79b65 commit d406eb1
Show file tree
Hide file tree
Showing 26 changed files with 889 additions and 520 deletions.
8 changes: 5 additions & 3 deletions ortools/sat/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ cc_library(
"//ortools/port:proto_utils",
"//ortools/util:saturated_arithmetic",
"//ortools/util:sorted_interval_list",
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
Expand Down Expand Up @@ -2926,9 +2927,7 @@ cc_library(
"//ortools/base",
"//ortools/base:stl_util",
"//ortools/base:strong_vector",
"//ortools/graph",
"//ortools/graph:connected_components",
"//ortools/graph:minimum_spanning_tree",
"//ortools/graph:strongly_connected_components",
"//ortools/util:fixed_shape_binary_tree",
"//ortools/util:integer_pq",
Expand Down Expand Up @@ -3110,9 +3109,11 @@ cc_test(
":integer_base",
":util",
"//ortools/base",
"//ortools/base:gmock_main",
"//ortools/base:fuzztest",
"//ortools/base:gmock",
"//ortools/graph:connected_components",
"//ortools/graph:strongly_connected_components",
"//ortools/util:saturated_arithmetic",
"//ortools/util:strong_integers",
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/container:flat_hash_map",
Expand All @@ -3123,6 +3124,7 @@ cc_test(
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:span",
"@com_google_benchmark//:benchmark",
"@com_google_fuzztest//fuzztest:fuzztest_gtest_main",
],
)

Expand Down
35 changes: 28 additions & 7 deletions ortools/sat/cp_model_checker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
#include <cmath>
#include <cstdint>
#include <limits>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include "absl/algorithm/container.h"
#include "absl/container/btree_map.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
Expand Down Expand Up @@ -1420,6 +1422,7 @@ class ConstraintChecker {
const auto& arg = ct.no_overlap_2d();
// Those intervals from arg.x_intervals and arg.y_intervals where both
// the x and y intervals are enforced.
bool has_zero_sizes = false;
std::vector<Rectangle> enforced_rectangles;
{
const int num_intervals = arg.x_intervals_size();
Expand All @@ -1432,16 +1435,34 @@ class ConstraintChecker {
.x_max = IntervalEnd(x.interval()),
.y_min = IntervalStart(y.interval()),
.y_max = IntervalEnd(y.interval())});
const auto& rect = enforced_rectangles.back();
if (rect.x_min == rect.x_max || rect.y_min == rect.y_max) {
has_zero_sizes = true;
}
}
}
}
const std::vector<std::pair<int, int>> intersections =
FindPartialRectangleIntersectionsAlsoEmpty(enforced_rectangles);
if (!intersections.empty()) {
VLOG(1) << "Rectangles " << intersections[0].first << "("
<< enforced_rectangles[intersections[0].first] << ") and "
<< intersections[0].second << "("
<< enforced_rectangles[intersections[0].second]

std::optional<std::pair<int, int>> one_intersection;
if (!has_zero_sizes) {
absl::c_stable_sort(enforced_rectangles,
[](const Rectangle& a, const Rectangle& b) {
return a.x_min < b.x_min;
});
one_intersection = FindOneIntersectionIfPresent(enforced_rectangles);
} else {
const std::vector<std::pair<int, int>> intersections =
FindPartialRectangleIntersections(enforced_rectangles);
if (!intersections.empty()) {
one_intersection = intersections[0];
}
}

if (one_intersection != std::nullopt) {
VLOG(1) << "Rectangles " << one_intersection->first << "("
<< enforced_rectangles[one_intersection->first] << ") and "
<< one_intersection->second << "("
<< enforced_rectangles[one_intersection->second]
<< ") are not disjoint.";
return false;
}
Expand Down
14 changes: 12 additions & 2 deletions ortools/sat/cp_model_lns.cc
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,7 @@ NeighborhoodGeneratorHelper::GetActiveRectangles(
}

std::vector<ActiveRectangle> results;
results.reserve(active_rectangles.size());
for (const auto& [rectangle, no_overlap_2d_constraints] : active_rectangles) {
ActiveRectangle& result = results.emplace_back();
result.x_interval = rectangle.first;
Expand Down Expand Up @@ -532,7 +533,9 @@ void RestrictAffineExpression(const LinearExpressionProto& expr,
const Domain domain =
ReadDomainFromProto(mutable_proto->variables(expr.vars(0)))
.IntersectionWith(implied_domain);
FillDomainInProto(domain, mutable_proto->mutable_variables(expr.vars(0)));
if (!domain.IsEmpty()) {
FillDomainInProto(domain, mutable_proto->mutable_variables(expr.vars(0)));
}
}

struct StartEndIndex {
Expand Down Expand Up @@ -1034,7 +1037,14 @@ std::vector<std::vector<int>> NeighborhoodGeneratorHelper::GetRoutingPaths(
Neighborhood NeighborhoodGeneratorHelper::FixGivenVariables(
const CpSolverResponse& base_solution,
const absl::flat_hash_set<int>& variables_to_fix) const {
Neighborhood neighborhood;
int initial_num_variables = 0;
{
absl::ReaderMutexLock domain_lock(&domain_mutex_);

initial_num_variables =
model_proto_with_only_variables_->variables().size();
}
Neighborhood neighborhood(initial_num_variables);

// TODO(user): Maybe relax all variables in the objective when the number
// is small or negligible compared to the number of variables.
Expand Down
12 changes: 11 additions & 1 deletion ortools/sat/cp_model_lns.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ namespace sat {

// Neighborhood returned by Neighborhood generators.
struct Neighborhood {
static constexpr int kDefaultArenaSizePerVariable = 128;

explicit Neighborhood(int num_variables_hint = 10)
: arena_buffer(kDefaultArenaSizePerVariable * num_variables_hint),
arena(std::make_unique<google::protobuf::Arena>(arena_buffer.data(),
arena_buffer.size())),
delta(*google::protobuf::Arena::Create<CpModelProto>(arena.get())) {}

// True if neighborhood generator was able to generate a neighborhood.
bool is_generated = false;

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

// Neighborhood Id. Used to identify the neighborhood by a generator.
// Currently only used by WeightedRandomRelaxationNeighborhoodGenerator.
Expand Down
Loading

0 comments on commit d406eb1

Please sign in to comment.