Skip to content

Commit dbe123f

Browse files
committed
[CP-SAT] more spans; one more case of preserving hints during presolve; new packing LNS; propagate objective in LNS
1 parent 79e3fc9 commit dbe123f

23 files changed

+801
-1191
lines changed

ortools/sat/2d_rectangle_presolve_test.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,7 @@ ShapePath TraceBoundary(
702702

703703
std::string RenderShapes(std::optional<Rectangle> bb,
704704
absl::Span<const Rectangle> rectangles,
705-
const std::vector<SingleShape>& shapes) {
705+
absl::Span<const SingleShape> shapes) {
706706
const std::vector<std::string> colors = {"black", "white", "orange",
707707
"cyan", "yellow", "purple"};
708708
std::stringstream ss;

ortools/sat/BUILD.bazel

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,7 @@ cc_library(
949949
"//ortools/util:logging",
950950
"//ortools/util:sorted_interval_list",
951951
"@com_google_absl//absl/log:check",
952+
"@com_google_absl//absl/types:span",
952953
],
953954
)
954955

@@ -1442,6 +1443,7 @@ cc_library(
14421443
":cp_model_cc_proto",
14431444
":cp_model_utils",
14441445
":integer",
1446+
":integer_base",
14451447
":presolve_context",
14461448
":presolve_util",
14471449
":util",
@@ -1469,7 +1471,7 @@ cc_test(
14691471
srcs = ["var_domination_test.cc"],
14701472
deps = [
14711473
":cp_model_cc_proto",
1472-
":integer",
1474+
":integer_base",
14731475
":model",
14741476
":presolve_context",
14751477
":var_domination",
@@ -3207,6 +3209,7 @@ cc_library(
32073209
"@com_google_absl//absl/container:flat_hash_set",
32083210
"@com_google_absl//absl/log:check",
32093211
"@com_google_absl//absl/meta:type_traits",
3212+
"@com_google_absl//absl/types:span",
32103213
],
32113214
)
32123215

ortools/sat/all_different.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ namespace operations_research {
3636
namespace sat {
3737

3838
std::function<void(Model*)> AllDifferentBinary(
39-
const std::vector<IntegerVariable>& vars) {
40-
return [=](Model* model) {
39+
absl::Span<const IntegerVariable> vars) {
40+
return [=, vars = std::vector<IntegerVariable>(vars.begin(), vars.end())](
41+
Model* model) {
4142
// Fully encode all the given variables and construct a mapping value ->
4243
// List of literal each indicating that a given variable takes this value.
4344
//
@@ -97,8 +98,9 @@ std::function<void(Model*)> AllDifferentOnBounds(
9798
}
9899

99100
std::function<void(Model*)> AllDifferentAC(
100-
const std::vector<IntegerVariable>& variables) {
101-
return [=](Model* model) {
101+
absl::Span<const IntegerVariable> variables) {
102+
return [=, variables = std::vector<IntegerVariable>(
103+
variables.begin(), variables.end())](Model* model) {
102104
if (variables.size() < 3) return;
103105

104106
AllDifferentConstraint* constraint = new AllDifferentConstraint(

ortools/sat/all_different.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ namespace sat {
3636
// encodes all the variables and simply enforces a <= 1 constraint on each
3737
// possible values.
3838
std::function<void(Model*)> AllDifferentBinary(
39-
const std::vector<IntegerVariable>& vars);
39+
absl::Span<const IntegerVariable> vars);
4040

4141
// Enforces that the given tuple of variables takes different values.
4242
// Same as AllDifferentBinary() but use a different propagator that only enforce
@@ -62,7 +62,7 @@ std::function<void(Model*)> AllDifferentOnBounds(
6262
//
6363
// This will fully encode variables.
6464
std::function<void(Model*)> AllDifferentAC(
65-
const std::vector<IntegerVariable>& variables);
65+
absl::Span<const IntegerVariable> variables);
6666

6767
// Implementation of AllDifferentAC().
6868
class AllDifferentConstraint : PropagatorInterface {

ortools/sat/circuit.cc

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "absl/container/flat_hash_set.h"
2222
#include "absl/log/check.h"
2323
#include "absl/meta/type_traits.h"
24+
#include "absl/types/span.h"
2425
#include "ortools/base/logging.h"
2526
#include "ortools/graph/strongly_connected_components.h"
2627
#include "ortools/sat/integer.h"
@@ -33,9 +34,9 @@ namespace operations_research {
3334
namespace sat {
3435

3536
CircuitPropagator::CircuitPropagator(const int num_nodes,
36-
const std::vector<int>& tails,
37-
const std::vector<int>& heads,
38-
const std::vector<Literal>& literals,
37+
absl::Span<const int> tails,
38+
absl::Span<const int> heads,
39+
absl::Span<const Literal> literals,
3940
Options options, Model* model)
4041
: num_nodes_(num_nodes),
4142
options_(options),
@@ -483,7 +484,7 @@ bool NoCyclePropagator::Propagate() {
483484

484485
CircuitCoveringPropagator::CircuitCoveringPropagator(
485486
std::vector<std::vector<Literal>> graph,
486-
const std::vector<int>& distinguished_nodes, Model* model)
487+
absl::Span<const int> distinguished_nodes, Model* model)
487488
: graph_(std::move(graph)),
488489
num_nodes_(graph_.size()),
489490
trail_(model->GetOrCreate<Trail>()) {
@@ -626,8 +627,9 @@ bool CircuitCoveringPropagator::Propagate() {
626627
}
627628

628629
std::function<void(Model*)> ExactlyOnePerRowAndPerColumn(
629-
const std::vector<std::vector<Literal>>& graph) {
630-
return [=](Model* model) {
630+
absl::Span<const std::vector<Literal>> graph) {
631+
return [=, graph = std::vector<std::vector<Literal>>(
632+
graph.begin(), graph.end())](Model* model) {
631633
const int n = graph.size();
632634
std::vector<Literal> exactly_one_constraint;
633635
exactly_one_constraint.reserve(n);
@@ -695,9 +697,10 @@ void LoadSubcircuitConstraint(int num_nodes, const std::vector<int>& tails,
695697
}
696698

697699
std::function<void(Model*)> CircuitCovering(
698-
const std::vector<std::vector<Literal>>& graph,
700+
absl::Span<const std::vector<Literal>> graph,
699701
const std::vector<int>& distinguished_nodes) {
700-
return [=](Model* model) {
702+
return [=, graph = std::vector<std::vector<Literal>>(
703+
graph.begin(), graph.end())](Model* model) {
701704
CircuitCoveringPropagator* constraint =
702705
new CircuitCoveringPropagator(graph, distinguished_nodes, model);
703706
constraint->RegisterWith(model->GetOrCreate<GenericLiteralWatcher>());

ortools/sat/circuit.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include "absl/container/btree_set.h"
2323
#include "absl/container/flat_hash_map.h"
24+
#include "absl/types/span.h"
2425
#include "ortools/base/logging.h"
2526
#include "ortools/base/types.h"
2627
#include "ortools/graph/strongly_connected_components.h"
@@ -53,9 +54,9 @@ class CircuitPropagator : PropagatorInterface, ReversibleInterface {
5354

5455
// The constraints take a sparse representation of a graph on [0, n). Each arc
5556
// being present when the given literal is true.
56-
CircuitPropagator(int num_nodes, const std::vector<int>& tails,
57-
const std::vector<int>& heads,
58-
const std::vector<Literal>& literals, Options options,
57+
CircuitPropagator(int num_nodes, absl::Span<const int> tails,
58+
absl::Span<const int> heads,
59+
absl::Span<const Literal> literals, Options options,
5960
Model* model);
6061

6162
// This type is neither copyable nor movable.
@@ -173,7 +174,7 @@ class NoCyclePropagator : PropagatorInterface, ReversibleInterface {
173174
class CircuitCoveringPropagator : PropagatorInterface, ReversibleInterface {
174175
public:
175176
CircuitCoveringPropagator(std::vector<std::vector<Literal>> graph,
176-
const std::vector<int>& distinguished_nodes,
177+
absl::Span<const int> distinguished_nodes,
177178
Model* model);
178179

179180
void SetLevel(int level) final;
@@ -254,9 +255,9 @@ void LoadSubcircuitConstraint(int num_nodes, const std::vector<int>& tails,
254255

255256
// TODO(user): Change to a sparse API like for the function above.
256257
std::function<void(Model*)> ExactlyOnePerRowAndPerColumn(
257-
const std::vector<std::vector<Literal>>& graph);
258+
absl::Span<const std::vector<Literal>> graph);
258259
std::function<void(Model*)> CircuitCovering(
259-
const std::vector<std::vector<Literal>>& graph,
260+
absl::Span<const std::vector<Literal>> graph,
260261
const std::vector<int>& distinguished_nodes);
261262

262263
} // namespace sat

ortools/sat/cp_constraints.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,13 @@ inline std::function<void(Model*)> GreaterThanAtLeastOneOf(
161161
// Note(user): If there is just one or two candidates, this doesn't add
162162
// anything.
163163
inline std::function<void(Model*)> PartialIsOneOfVar(
164-
IntegerVariable target_var, const std::vector<IntegerVariable>& vars,
165-
const std::vector<Literal>& selectors) {
164+
IntegerVariable target_var, absl::Span<const IntegerVariable> vars,
165+
absl::Span<const Literal> selectors) {
166166
CHECK_EQ(vars.size(), selectors.size());
167-
return [=](Model* model) {
167+
return [=,
168+
selectors = std::vector<Literal>(selectors.begin(), selectors.end()),
169+
vars = std::vector<IntegerVariable>(vars.begin(), vars.end())](
170+
Model* model) {
168171
const std::vector<IntegerValue> offsets(vars.size(), IntegerValue(0));
169172
if (vars.size() > 2) {
170173
// Propagate the min.

ortools/sat/cp_model_expand.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,8 +1351,8 @@ void ExpandNegativeTable(ConstraintProto* ct, PresolveContext* context) {
13511351
// We list for each tuple the possible values the variable can take.
13521352
// If the list is empty, then this encode "any value".
13531353
void ProcessOneCompressedColumn(
1354-
int variable, const std::vector<int>& tuple_literals,
1355-
const std::vector<absl::InlinedVector<int64_t, 2>>& values,
1354+
int variable, absl::Span<const int> tuple_literals,
1355+
absl::Span<const absl::InlinedVector<int64_t, 2>> values,
13561356
std::optional<int> table_is_active_literal, PresolveContext* context) {
13571357
DCHECK_EQ(tuple_literals.size(), values.size());
13581358

@@ -1656,7 +1656,7 @@ bool ReduceTableInPresenceOfUniqueVariableWithCosts(
16561656
// is called. Some checks will fail otherwise.
16571657
void CompressAndExpandPositiveTable(ConstraintProto* ct,
16581658
bool last_column_is_cost,
1659-
const std::vector<int>& vars,
1659+
absl::Span<const int> vars,
16601660
std::vector<std::vector<int64_t>>* tuples,
16611661
PresolveContext* context) {
16621662
const int num_tuples_before_compression = tuples->size();

ortools/sat/cp_model_lns.cc

Lines changed: 88 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,7 @@ struct IndexedRectangle {
825825
};
826826

827827
void InsertRectanglePredecences(
828-
const std::vector<IndexedRectangle>& rectangles,
828+
absl::Span<const IndexedRectangle> rectangles,
829829
absl::flat_hash_set<std::pair<int, int>>* precedences) {
830830
// TODO(user): Refine set of interesting points.
831831
std::vector<IntegerValue> interesting_points;
@@ -1148,7 +1148,7 @@ void NeighborhoodGeneratorHelper::AddSolutionHinting(
11481148

11491149
Neighborhood NeighborhoodGeneratorHelper::RelaxGivenVariables(
11501150
const CpSolverResponse& initial_solution,
1151-
const std::vector<int>& relaxed_variables) const {
1151+
absl::Span<const int> relaxed_variables) const {
11521152
std::vector<bool> relaxed_variables_set(model_proto_.variables_size(), false);
11531153
for (const int var : relaxed_variables) relaxed_variables_set[var] = true;
11541154
absl::flat_hash_set<int> fixed_variables;
@@ -1737,8 +1737,8 @@ namespace {
17371737

17381738
// Create a constraint sum (X - LB) + sum (UB - X) <= rhs.
17391739
ConstraintProto DistanceToBoundsSmallerThanConstraint(
1740-
const std::vector<std::pair<int, int64_t>>& dist_to_lower_bound,
1741-
const std::vector<std::pair<int, int64_t>>& dist_to_upper_bound,
1740+
absl::Span<const std::pair<int, int64_t>> dist_to_lower_bound,
1741+
absl::Span<const std::pair<int, int64_t>> dist_to_upper_bound,
17421742
const int64_t rhs) {
17431743
DCHECK_GE(rhs, 0);
17441744
ConstraintProto new_constraint;
@@ -2290,6 +2290,84 @@ Neighborhood RandomRectanglesPackingNeighborhoodGenerator::Generate(
22902290
return helper_.FixGivenVariables(initial_solution, variables_to_freeze);
22912291
}
22922292

2293+
Neighborhood RectanglesPackingRelaxOneNeighborhoodGenerator::Generate(
2294+
const CpSolverResponse& initial_solution, SolveData& data,
2295+
absl::BitGenRef random) {
2296+
// First pick one rectangle.
2297+
const std::vector<ActiveRectangle> all_active_rectangles =
2298+
helper_.GetActiveRectangles(initial_solution);
2299+
if (all_active_rectangles.size() <= 1) return helper_.FullNeighborhood();
2300+
2301+
const ActiveRectangle& base_rectangle =
2302+
all_active_rectangles[absl::Uniform<int>(random, 0,
2303+
all_active_rectangles.size())];
2304+
2305+
const auto get_rectangle = [&initial_solution, helper = &helper_](
2306+
const ActiveRectangle& rectangle) {
2307+
const int x_interval_idx = rectangle.x_interval;
2308+
const int y_interval_idx = rectangle.y_interval;
2309+
const ConstraintProto& x_interval_ct =
2310+
helper->ModelProto().constraints(x_interval_idx);
2311+
const ConstraintProto& y_interval_ct =
2312+
helper->ModelProto().constraints(y_interval_idx);
2313+
return Rectangle{.x_min = GetLinearExpressionValue(
2314+
x_interval_ct.interval().start(), initial_solution),
2315+
.x_max = GetLinearExpressionValue(
2316+
x_interval_ct.interval().end(), initial_solution),
2317+
.y_min = GetLinearExpressionValue(
2318+
y_interval_ct.interval().start(), initial_solution),
2319+
.y_max = GetLinearExpressionValue(
2320+
y_interval_ct.interval().end(), initial_solution)};
2321+
};
2322+
2323+
const Rectangle center_rect = get_rectangle(base_rectangle);
2324+
2325+
// Now compute a neighborhood around that rectangle. In this neighborhood
2326+
// we prefer a "Square" region around the initial rectangle center rather than
2327+
// a circle.
2328+
//
2329+
// Note that we only consider two rectangles as potential neighbors if they
2330+
// are part of the same no_overlap_2d constraint.
2331+
absl::flat_hash_set<int> variables_to_freeze;
2332+
std::vector<std::pair<int, double>> distances;
2333+
distances.reserve(all_active_rectangles.size());
2334+
for (int i = 0; i < all_active_rectangles.size(); ++i) {
2335+
const ActiveRectangle& rectangle = all_active_rectangles[i];
2336+
InsertVariablesFromConstraint(helper_.ModelProto(), rectangle.x_interval,
2337+
variables_to_freeze);
2338+
InsertVariablesFromConstraint(helper_.ModelProto(), rectangle.y_interval,
2339+
variables_to_freeze);
2340+
2341+
const Rectangle rect = get_rectangle(rectangle);
2342+
const bool same_no_overlap_as_center_rect = absl::c_any_of(
2343+
base_rectangle.no_overlap_2d_constraints, [&rectangle](const int c) {
2344+
return rectangle.no_overlap_2d_constraints.contains(c);
2345+
});
2346+
if (same_no_overlap_as_center_rect) {
2347+
distances.push_back(
2348+
{i, CenterToCenterLInfinityDistance(center_rect, rect)});
2349+
}
2350+
}
2351+
std::sort(distances.begin(), distances.end(),
2352+
[](const auto& a, const auto& b) { return a.second < b.second; });
2353+
2354+
const int num_to_sample = data.difficulty * all_active_rectangles.size();
2355+
absl::flat_hash_set<int> variables_to_relax;
2356+
const int num_to_relax = std::min<int>(distances.size(), num_to_sample);
2357+
for (int i = 0; i < num_to_relax; ++i) {
2358+
const int rectangle_idx = distances[i].first;
2359+
const ActiveRectangle& rectangle = all_active_rectangles[rectangle_idx];
2360+
InsertVariablesFromConstraint(helper_.ModelProto(), rectangle.x_interval,
2361+
variables_to_relax);
2362+
InsertVariablesFromConstraint(helper_.ModelProto(), rectangle.y_interval,
2363+
variables_to_relax);
2364+
}
2365+
for (const int v : variables_to_relax) {
2366+
variables_to_freeze.erase(v);
2367+
}
2368+
return helper_.FixGivenVariables(initial_solution, variables_to_freeze);
2369+
}
2370+
22932371
Neighborhood RectanglesPackingRelaxTwoNeighborhoodsGenerator::Generate(
22942372
const CpSolverResponse& initial_solution, SolveData& data,
22952373
absl::BitGenRef random) {
@@ -2327,22 +2405,16 @@ Neighborhood RectanglesPackingRelaxTwoNeighborhoodsGenerator::Generate(
23272405
y_interval_ct.interval().end(), initial_solution)};
23282406
};
23292407

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

23432411
// Now compute a neighborhood around each rectangle. Note that we only
23442412
// consider two rectangles as potential neighbors if they are part of the same
23452413
// no_overlap_2d constraint.
2414+
//
2415+
// TODO(user): This computes the distance between the center of the
2416+
// rectangles. We could use the real distance between the closest points, but
2417+
// not sure it is worth the extra complexity.
23462418
absl::flat_hash_set<int> variables_to_freeze;
23472419
std::vector<std::pair<int, double>> distances1;
23482420
std::vector<std::pair<int, double>> distances2;
@@ -2367,10 +2439,10 @@ Neighborhood RectanglesPackingRelaxTwoNeighborhoodsGenerator::Generate(
23672439
return rectangle.no_overlap_2d_constraints.contains(c);
23682440
});
23692441
if (same_no_overlap_as_rect1) {
2370-
distances1.push_back({i, compute_rectangle_distance(rect1, rect)});
2442+
distances1.push_back({i, CenterToCenterL2Distance(rect1, rect)});
23712443
}
23722444
if (same_no_overlap_as_rect2) {
2373-
distances2.push_back({i, compute_rectangle_distance(rect2, rect)});
2445+
distances2.push_back({i, CenterToCenterL2Distance(rect2, rect)});
23742446
}
23752447
}
23762448
const int num_to_sample_each =

0 commit comments

Comments
 (0)