@@ -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+
525538struct 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
23712435Neighborhood RectanglesPackingRelaxTwoNeighborhoodsGenerator::Generate (
0 commit comments