Skip to content

Commit cb9f54e

Browse files
committed
[CP-SAT] remove contention; fix bug with hints and non contiguous domains
1 parent 66dd2cb commit cb9f54e

File tree

2 files changed

+78
-14
lines changed

2 files changed

+78
-14
lines changed

ortools/sat/cp_model_lns.cc

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ void NeighborhoodGeneratorHelper::Synchronize() {
102102

103103
bool new_variables_have_been_fixed = false;
104104

105-
{
105+
if (!model_variables.empty()) {
106106
absl::MutexLock domain_lock(&domain_mutex_);
107107

108108
for (int i = 0; i < model_variables.size(); ++i) {
@@ -522,6 +522,19 @@ int64_t GetLinearExpressionValue(const LinearExpressionProto& expr,
522522
return result;
523523
}
524524

525+
void RestrictAffineExpression(const LinearExpressionProto& expr,
526+
const Domain& restriction,
527+
CpModelProto* mutable_proto) {
528+
CHECK_LE(expr.vars().size(), 1);
529+
if (expr.vars().empty()) return;
530+
const Domain implied_domain = restriction.AdditionWith(Domain(-expr.offset()))
531+
.InverseMultiplicationBy(expr.coeffs(0));
532+
const Domain domain =
533+
ReadDomainFromProto(mutable_proto->variables(expr.vars(0)))
534+
.IntersectionWith(implied_domain);
535+
FillDomainInProto(domain, mutable_proto->mutable_variables(expr.vars(0)));
536+
}
537+
525538
struct StartEndIndex {
526539
int64_t start;
527540
int64_t end;
@@ -2348,24 +2361,75 @@ Neighborhood RectanglesPackingRelaxOneNeighborhoodGenerator::Generate(
23482361
{i, CenterToCenterLInfinityDistance(center_rect, rect)});
23492362
}
23502363
}
2351-
std::sort(distances.begin(), distances.end(),
2352-
[](const auto& a, const auto& b) { return a.second < b.second; });
2364+
std::stable_sort(
2365+
distances.begin(), distances.end(),
2366+
[](const auto& a, const auto& b) { return a.second < b.second; });
23532367

23542368
const int num_to_sample = data.difficulty * all_active_rectangles.size();
2355-
absl::flat_hash_set<int> variables_to_relax;
23562369
const int num_to_relax = std::min<int>(distances.size(), num_to_sample);
2370+
Rectangle relaxed_bounding_box = center_rect;
2371+
absl::flat_hash_set<int> boxes_to_relax;
23572372
for (int i = 0; i < num_to_relax; ++i) {
23582373
const int rectangle_idx = distances[i].first;
23592374
const ActiveRectangle& rectangle = all_active_rectangles[rectangle_idx];
2375+
relaxed_bounding_box.GrowToInclude(get_rectangle(rectangle));
2376+
boxes_to_relax.insert(rectangle_idx);
2377+
}
2378+
2379+
// Heuristic: we relax a bit the bounding box in order to allow some
2380+
// movements, this is needed to not have a trivial neighborhood if we relax a
2381+
// single box for instance.
2382+
const IntegerValue x_size = relaxed_bounding_box.SizeX();
2383+
const IntegerValue y_size = relaxed_bounding_box.SizeY();
2384+
relaxed_bounding_box.x_min = CapSubI(relaxed_bounding_box.x_min, x_size / 2);
2385+
relaxed_bounding_box.x_max = CapAddI(relaxed_bounding_box.x_max, x_size / 2);
2386+
relaxed_bounding_box.y_min = CapSubI(relaxed_bounding_box.y_min, y_size / 2);
2387+
relaxed_bounding_box.y_max = CapAddI(relaxed_bounding_box.y_max, y_size / 2);
2388+
2389+
for (const int b : boxes_to_relax) {
2390+
const ActiveRectangle& rectangle = all_active_rectangles[b];
2391+
absl::flat_hash_set<int> variables_to_relax;
23602392
InsertVariablesFromConstraint(helper_.ModelProto(), rectangle.x_interval,
23612393
variables_to_relax);
23622394
InsertVariablesFromConstraint(helper_.ModelProto(), rectangle.y_interval,
23632395
variables_to_relax);
2396+
for (const int v : variables_to_relax) {
2397+
variables_to_freeze.erase(v);
2398+
}
23642399
}
2365-
for (const int v : variables_to_relax) {
2366-
variables_to_freeze.erase(v);
2400+
Neighborhood neighborhood =
2401+
helper_.FixGivenVariables(initial_solution, variables_to_freeze);
2402+
2403+
// The call above add the relaxed variables to the neighborhood using the
2404+
// current bounds at level 0. For big problems, this might create a hard model
2405+
// with a large complicated landscape of fixed boxes with a lot of potential
2406+
// places to place the relaxed boxes. Therefore we update the domain so the
2407+
// boxes can only stay around the area we decided to relax.
2408+
for (const int b : boxes_to_relax) {
2409+
{
2410+
const IntervalConstraintProto& x_interval =
2411+
helper_.ModelProto()
2412+
.constraints(all_active_rectangles[b].x_interval)
2413+
.interval();
2414+
const Domain x_domain = Domain(relaxed_bounding_box.x_min.value(),
2415+
relaxed_bounding_box.x_max.value());
2416+
RestrictAffineExpression(x_interval.start(), x_domain,
2417+
&neighborhood.delta);
2418+
RestrictAffineExpression(x_interval.end(), x_domain, &neighborhood.delta);
2419+
}
2420+
{
2421+
const IntervalConstraintProto& y_interval =
2422+
helper_.ModelProto()
2423+
.constraints(all_active_rectangles[b].y_interval)
2424+
.interval();
2425+
const Domain y_domain = Domain(relaxed_bounding_box.y_min.value(),
2426+
relaxed_bounding_box.y_max.value());
2427+
RestrictAffineExpression(y_interval.start(), y_domain,
2428+
&neighborhood.delta);
2429+
RestrictAffineExpression(y_interval.end(), y_domain, &neighborhood.delta);
2430+
}
23672431
}
2368-
return helper_.FixGivenVariables(initial_solution, variables_to_freeze);
2432+
return neighborhood;
23692433
}
23702434

23712435
Neighborhood RectanglesPackingRelaxTwoNeighborhoodsGenerator::Generate(

ortools/sat/var_domination.cc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1489,14 +1489,14 @@ void MaybeUpdateRefHintFromDominance(
14891489
const std::optional<int64_t> dominating_ref_hint =
14901490
context.GetRefSolutionHint(dominating_ref);
14911491
if (!dominating_ref_hint.has_value()) continue;
1492-
const int64_t delta =
1493-
context.DomainOf(dominating_ref)
1494-
.ClosestValue(*dominating_ref_hint + remaining_delta) -
1495-
*dominating_ref_hint;
1492+
const Domain& dominating_ref_domain = context.DomainOf(dominating_ref);
1493+
const int64_t new_dominating_ref_hint =
1494+
dominating_ref_domain.ValueAtOrBefore(*dominating_ref_hint +
1495+
remaining_delta);
14961496
// This might happen if the solution hint is not initially feasible.
1497-
if (delta < 0) continue;
1498-
context.UpdateRefSolutionHint(dominating_ref, *dominating_ref_hint + delta);
1499-
remaining_delta -= delta;
1497+
if (!dominating_ref_domain.Contains(new_dominating_ref_hint)) continue;
1498+
context.UpdateRefSolutionHint(dominating_ref, new_dominating_ref_hint);
1499+
remaining_delta -= (new_dominating_ref_hint - *dominating_ref_hint);
15001500
if (remaining_delta == 0) break;
15011501
}
15021502
}

0 commit comments

Comments
 (0)