Skip to content

Commit

Permalink
sat: Export from google3
Browse files Browse the repository at this point in the history
  • Loading branch information
Mizux committed Oct 7, 2024
1 parent ed1adfb commit bda54b7
Show file tree
Hide file tree
Showing 12 changed files with 1,082 additions and 107 deletions.
5 changes: 3 additions & 2 deletions ortools/sat/2d_rectangle_presolve_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,14 @@ using ::testing::IsEmpty;
std::vector<Rectangle> BuildFromAsciiArt(std::string_view input) {
std::vector<Rectangle> rectangles;
std::vector<std::string_view> lines = absl::StrSplit(input, '\n');
const int num_lines = lines.size();
for (int i = 0; i < lines.size(); i++) {
for (int j = 0; j < lines[i].size(); j++) {
if (lines[i][j] != ' ') {
rectangles.push_back({.x_min = j,
.x_max = j + 1,
.y_min = 2 * lines.size() - 2 * i,
.y_max = 2 * lines.size() - 2 * i + 2});
.y_min = 2 * num_lines - 2 * i,
.y_max = 2 * num_lines - 2 * i + 2});
}
}
}
Expand Down
22 changes: 22 additions & 0 deletions ortools/sat/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -2146,6 +2146,7 @@ cc_library(
"//ortools/lp_data:base",
"//ortools/lp_data:lp_data_utils",
"//ortools/lp_data:scattered_vector",
"//ortools/lp_data:sparse",
"//ortools/lp_data:sparse_column",
"//ortools/util:bitset",
"//ortools/util:rev",
Expand All @@ -2156,6 +2157,7 @@ cc_library(
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/numeric:int128",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/types:span",
],
)
Expand Down Expand Up @@ -3404,6 +3406,26 @@ cc_library(
],
)

cc_test(
name = "work_assignment_test",
srcs = ["work_assignment_test.cc"],
deps = [
":cp_model",
":cp_model_cc_proto",
":cp_model_checker",
":cp_model_loader",
":cp_model_solver",
":integer",
":model",
":sat_parameters_cc_proto",
":synchronization",
":work_assignment",
"//ortools/base:gmock_main",
"//ortools/base:parse_text_proto",
"@com_google_absl//absl/strings:string_view",
],
)

cc_test(
name = "inclusion_test",
size = "small",
Expand Down
6 changes: 3 additions & 3 deletions ortools/sat/cp_model_checker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -854,9 +854,9 @@ std::string ValidateSolutionHint(const CpModelProto& model) {
if (hint.vars().size() != hint.values().size()) {
return "Invalid solution hint: vars and values do not have the same size.";
}
for (const int ref : hint.vars()) {
if (!VariableReferenceIsValid(model, ref)) {
return absl::StrCat("Invalid variable reference in solution hint: ", ref);
for (const int var : hint.vars()) {
if (!VariableIndexIsValid(model, var)) {
return absl::StrCat("Invalid variable in solution hint: ", var);
}
}

Expand Down
154 changes: 137 additions & 17 deletions ortools/sat/cp_model_lns.cc
Original file line number Diff line number Diff line change
Expand Up @@ -448,15 +448,15 @@ std::vector<int> NeighborhoodGeneratorHelper::GetActiveIntervals(
initial_solution);
}

std::vector<std::pair<int, int>>
std::vector<NeighborhoodGeneratorHelper::ActiveRectangle>
NeighborhoodGeneratorHelper::GetActiveRectangles(
const CpSolverResponse& initial_solution) const {
const std::vector<int> active_intervals =
GetActiveIntervals(initial_solution);
const absl::flat_hash_set<int> active_intervals_set(active_intervals.begin(),
active_intervals.end());

std::vector<std::pair<int, int>> active_rectangles;
absl::flat_hash_map<std::pair<int, int>, std::vector<int>> active_rectangles;
for (const int ct_index : TypeToConstraints(ConstraintProto::kNoOverlap2D)) {
const NoOverlap2DConstraintProto& ct =
model_proto_.constraints(ct_index).no_overlap_2d();
Expand All @@ -465,12 +465,20 @@ NeighborhoodGeneratorHelper::GetActiveRectangles(
const int y_i = ct.y_intervals(i);
if (active_intervals_set.contains(x_i) ||
active_intervals_set.contains(y_i)) {
active_rectangles.push_back({x_i, y_i});
active_rectangles[{x_i, y_i}].push_back(ct_index);
}
}
}

return active_rectangles;
std::vector<ActiveRectangle> results;
for (const auto& [rectangle, no_overlap_2d_constraints] : active_rectangles) {
ActiveRectangle& result = results.emplace_back();
result.x_interval = rectangle.first;
result.y_interval = rectangle.second;
result.no_overlap_2d_constraints = {no_overlap_2d_constraints.begin(),
no_overlap_2d_constraints.end()};
}
return results;
}

std::vector<std::vector<int>>
Expand Down Expand Up @@ -2256,14 +2264,125 @@ Neighborhood SchedulingResourceWindowsNeighborhoodGenerator::Generate(
Neighborhood RandomRectanglesPackingNeighborhoodGenerator::Generate(
const CpSolverResponse& initial_solution, SolveData& data,
absl::BitGenRef random) {
std::vector<std::pair<int, int>> rectangles_to_freeze =
std::vector<ActiveRectangle> rectangles_to_freeze =
helper_.GetActiveRectangles(initial_solution);
GetRandomSubset(1.0 - data.difficulty, &rectangles_to_freeze, random);

absl::flat_hash_set<int> variables_to_freeze;
for (const auto& [x, y] : rectangles_to_freeze) {
InsertVariablesFromConstraint(helper_.ModelProto(), x, variables_to_freeze);
InsertVariablesFromConstraint(helper_.ModelProto(), y, variables_to_freeze);
for (const ActiveRectangle& rectangle : rectangles_to_freeze) {
InsertVariablesFromConstraint(helper_.ModelProto(), rectangle.x_interval,
variables_to_freeze);
InsertVariablesFromConstraint(helper_.ModelProto(), rectangle.y_interval,
variables_to_freeze);
}

return helper_.FixGivenVariables(initial_solution, variables_to_freeze);
}

Neighborhood RectanglesPackingRelaxTwoNeighborhoodsGenerator::Generate(
const CpSolverResponse& initial_solution, SolveData& data,
absl::BitGenRef random) {
// First pick a pair of rectangles.
std::vector<ActiveRectangle> all_active_rectangles =
helper_.GetActiveRectangles(initial_solution);
if (all_active_rectangles.size() <= 2) return helper_.FullNeighborhood();

const int first_idx =
absl::Uniform<int>(random, 0, all_active_rectangles.size());
int second_idx =
absl::Uniform<int>(random, 0, all_active_rectangles.size() - 1);
if (second_idx >= first_idx) {
second_idx++;
}

const ActiveRectangle& chosen_rectangle_1 = all_active_rectangles[first_idx];
const ActiveRectangle& chosen_rectangle_2 = all_active_rectangles[second_idx];

const auto get_rectangle = [&initial_solution, helper = &helper_](
const ActiveRectangle& rectangle) {
const int x_interval_idx = rectangle.x_interval;
const int y_interval_idx = rectangle.y_interval;
const ConstraintProto& x_interval_ct =
helper->ModelProto().constraints(x_interval_idx);
const ConstraintProto& y_interval_ct =
helper->ModelProto().constraints(y_interval_idx);
return Rectangle{.x_min = GetLinearExpressionValue(
x_interval_ct.interval().start(), initial_solution),
.x_max = GetLinearExpressionValue(
x_interval_ct.interval().end(), initial_solution),
.y_min = GetLinearExpressionValue(
y_interval_ct.interval().start(), initial_solution),
.y_max = GetLinearExpressionValue(
y_interval_ct.interval().end(), initial_solution)};
};

// TODO(user): This computes the distance between the center of the
// rectangles. We could use the real distance between the closest points, but
// not sure it is worth the extra complexity.
const auto compute_rectangle_distance = [](const Rectangle& rect1,
const Rectangle& rect2) {
return (static_cast<double>(rect1.x_min.value()) + rect1.x_max.value() -
rect2.x_min.value() - rect2.x_max.value()) *
(static_cast<double>(rect1.y_min.value()) + rect1.y_max.value() -
rect2.y_min.value() - rect2.y_max.value());
};
const Rectangle rect1 = get_rectangle(chosen_rectangle_1);
const Rectangle rect2 = get_rectangle(chosen_rectangle_2);

// Now compute a neighborhood around each rectangle. Note that we only
// consider two rectangles as potential neighbors if they are part of the same
// no_overlap_2d constraint.
absl::flat_hash_set<int> variables_to_freeze;
std::vector<std::pair<int, double>> distances1;
std::vector<std::pair<int, double>> distances2;
distances1.reserve(all_active_rectangles.size());
distances2.reserve(all_active_rectangles.size());
for (int i = 0; i < all_active_rectangles.size(); ++i) {
const ActiveRectangle& rectangle = all_active_rectangles[i];
InsertVariablesFromConstraint(helper_.ModelProto(), rectangle.x_interval,
variables_to_freeze);
InsertVariablesFromConstraint(helper_.ModelProto(), rectangle.y_interval,
variables_to_freeze);

const Rectangle rect = get_rectangle(rectangle);
const bool same_no_overlap_as_rect1 =
absl::c_any_of(chosen_rectangle_1.no_overlap_2d_constraints,
[&rectangle](const int c) {
return rectangle.no_overlap_2d_constraints.contains(c);
});
const bool same_no_overlap_as_rect2 =
absl::c_any_of(chosen_rectangle_2.no_overlap_2d_constraints,
[&rectangle](const int c) {
return rectangle.no_overlap_2d_constraints.contains(c);
});
if (same_no_overlap_as_rect1) {
distances1.push_back({i, compute_rectangle_distance(rect1, rect)});
}
if (same_no_overlap_as_rect2) {
distances2.push_back({i, compute_rectangle_distance(rect2, rect)});
}
}
const int num_to_sample_each =
data.difficulty * all_active_rectangles.size() / 2;
std::sort(distances1.begin(), distances1.end(),
[](const auto& a, const auto& b) { return a.second < b.second; });
std::sort(distances2.begin(), distances2.end(),
[](const auto& a, const auto& b) { return a.second < b.second; });
absl::flat_hash_set<int> variables_to_relax;
for (auto& samples : {distances1, distances2}) {
const int num_potential_samples = samples.size();
for (int i = 0; i < std::min(num_potential_samples, num_to_sample_each);
++i) {
const int rectangle_idx = samples[i].first;
const ActiveRectangle& rectangle = all_active_rectangles[rectangle_idx];
InsertVariablesFromConstraint(helper_.ModelProto(), rectangle.x_interval,
variables_to_relax);
InsertVariablesFromConstraint(helper_.ModelProto(), rectangle.y_interval,
variables_to_relax);
}
}
for (const int v : variables_to_relax) {
variables_to_freeze.erase(v);
}

return helper_.FixGivenVariables(initial_solution, variables_to_freeze);
Expand All @@ -2272,13 +2391,13 @@ Neighborhood RandomRectanglesPackingNeighborhoodGenerator::Generate(
Neighborhood RandomPrecedencesPackingNeighborhoodGenerator::Generate(
const CpSolverResponse& initial_solution, SolveData& data,
absl::BitGenRef random) {
std::vector<std::pair<int, int>> rectangles_to_relax =
std::vector<ActiveRectangle> rectangles_to_relax =
helper_.GetActiveRectangles(initial_solution);
GetRandomSubset(data.difficulty, &rectangles_to_relax, random);
std::vector<int> intervals_to_relax;
for (const auto& [x, y] : rectangles_to_relax) {
intervals_to_relax.push_back(x);
intervals_to_relax.push_back(y);
for (const ActiveRectangle& rect : rectangles_to_relax) {
intervals_to_relax.push_back(rect.x_interval);
intervals_to_relax.push_back(rect.y_interval);
}
gtl::STLSortAndRemoveDuplicates(&intervals_to_relax);

Expand All @@ -2289,13 +2408,14 @@ Neighborhood RandomPrecedencesPackingNeighborhoodGenerator::Generate(
Neighborhood SlicePackingNeighborhoodGenerator::Generate(
const CpSolverResponse& initial_solution, SolveData& data,
absl::BitGenRef random) {
const std::vector<std::pair<int, int>> active_rectangles =
const std::vector<ActiveRectangle> active_rectangles =
helper_.GetActiveRectangles(initial_solution);
const bool use_first_dimension = absl::Bernoulli(random, 0.5);
std::vector<int> projected_intervals;
projected_intervals.reserve(active_rectangles.size());
for (const auto& [x, y] : active_rectangles) {
projected_intervals.push_back(use_first_dimension ? x : y);
for (const ActiveRectangle& rect : active_rectangles) {
projected_intervals.push_back(use_first_dimension ? rect.x_interval
: rect.y_interval);
}

const TimePartition partition = PartitionIndicesAroundRandomTimeWindow(
Expand All @@ -2310,10 +2430,10 @@ Neighborhood SlicePackingNeighborhoodGenerator::Generate(
for (int index = 0; index < active_rectangles.size(); ++index) {
if (indices_to_fix[index]) {
InsertVariablesFromConstraint(helper_.ModelProto(),
active_rectangles[index].first,
active_rectangles[index].x_interval,
variables_to_freeze);
InsertVariablesFromConstraint(helper_.ModelProto(),
active_rectangles[index].second,
active_rectangles[index].y_interval,
variables_to_freeze);
}
}
Expand Down
26 changes: 25 additions & 1 deletion ortools/sat/cp_model_lns.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,14 @@ class NeighborhoodGeneratorHelper : public SubSolver {
// lns_focus_on_performed_intervals. If true, this method returns the list of
// performed rectangles in the solution. If false, it returns all rectangles
// of the model.
std::vector<std::pair<int, int>> GetActiveRectangles(
struct ActiveRectangle {
int x_interval;
int y_interval;
// The set of no_overlap_2d constraints that both x_interval and y_interval
// are participating in.
absl::flat_hash_set<int> no_overlap_2d_constraints;
};
std::vector<ActiveRectangle> GetActiveRectangles(
const CpSolverResponse& initial_solution) const;

// Returns the set of unique intervals list appearing in a no_overlap,
Expand Down Expand Up @@ -356,6 +363,8 @@ class NeighborhoodGenerator {
: name_(name), helper_(*helper), difficulty_(0.5) {}
virtual ~NeighborhoodGenerator() = default;

using ActiveRectangle = NeighborhoodGeneratorHelper::ActiveRectangle;

// Adds solve data about one "solved" neighborhood.
struct SolveData {
// The status of the sub-solve.
Expand Down Expand Up @@ -706,6 +715,21 @@ class RandomRectanglesPackingNeighborhoodGenerator
SolveData& data, absl::BitGenRef random) final;
};

// Only make sense for problems with no_overlap_2d constraints. This selects two
// random rectangles and relax them alongside the closest rectangles to each one
// of them. The idea is that this will find a better solution when there is a
// cost function that would be improved by swapping the two rectangles.
class RectanglesPackingRelaxTwoNeighborhoodsGenerator
: public NeighborhoodGenerator {
public:
explicit RectanglesPackingRelaxTwoNeighborhoodsGenerator(
NeighborhoodGeneratorHelper const* helper, absl::string_view name)
: NeighborhoodGenerator(name, helper) {}

Neighborhood Generate(const CpSolverResponse& initial_solution,
SolveData& data, absl::BitGenRef random) final;
};

// Only make sense for problems with no_overlap_2d constraints. This select a
// random set of rectangles (i.e. a pair of intervals) of the problem according
// to the difficulty. Then add all implied precedences from the current
Expand Down
Loading

0 comments on commit bda54b7

Please sign in to comment.