From b3e663de62dbc68c0e36b401e1c5a657ac220e31 Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Wed, 9 Aug 2023 14:49:24 -0700 Subject: [PATCH] [CP-SAT] add packing_precedences_lns --- ortools/sat/cp_model_lns.cc | 18 ++++++++++ ortools/sat/cp_model_lns.h | 17 +++++++++- ortools/sat/cp_model_solver.cc | 61 +++++++++++++++++++++------------- 3 files changed, 72 insertions(+), 24 deletions(-) diff --git a/ortools/sat/cp_model_lns.cc b/ortools/sat/cp_model_lns.cc index 79f25aec4f1..b6e3563cff5 100644 --- a/ortools/sat/cp_model_lns.cc +++ b/ortools/sat/cp_model_lns.cc @@ -60,6 +60,7 @@ #include "ortools/util/saturated_arithmetic.h" #include "ortools/util/sorted_interval_list.h" #include "ortools/util/strong_integers.h" +#include "ortools/util/time_limit.h" namespace operations_research { namespace sat { @@ -2061,6 +2062,23 @@ Neighborhood RandomRectanglesPackingNeighborhoodGenerator::Generate( return helper_.FixGivenVariables(initial_solution, variables_to_freeze); } +Neighborhood RandomPrecedencesPackingNeighborhoodGenerator::Generate( + const CpSolverResponse& initial_solution, double difficulty, + absl::BitGenRef random) { + std::vector> rectangles_to_relax = + helper_.GetActiveRectangles(initial_solution); + GetRandomSubset(difficulty, &rectangles_to_relax, random); + std::vector intervals_to_relax; + for (const auto& [x, y] : rectangles_to_relax) { + intervals_to_relax.push_back(x); + intervals_to_relax.push_back(y); + } + gtl::STLSortAndRemoveDuplicates(&intervals_to_relax); + + return GenerateSchedulingNeighborhoodFromRelaxedIntervals( + intervals_to_relax, initial_solution, random, helper_); +} + Neighborhood SlicePackingNeighborhoodGenerator::Generate( const CpSolverResponse& initial_solution, double difficulty, absl::BitGenRef random) { diff --git a/ortools/sat/cp_model_lns.h b/ortools/sat/cp_model_lns.h index f478bdf08dd..103f656508a 100644 --- a/ortools/sat/cp_model_lns.h +++ b/ortools/sat/cp_model_lns.h @@ -683,7 +683,7 @@ class SchedulingResourceWindowsNeighborhoodGenerator // 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, fix all variables in the selected intervals. +// to the difficulty. Then fix all variables in the selected intervals. class RandomRectanglesPackingNeighborhoodGenerator : public NeighborhoodGenerator { public: @@ -695,6 +695,21 @@ class RandomRectanglesPackingNeighborhoodGenerator double difficulty, 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 +// positions of the rectangles in this +class RandomPrecedencesPackingNeighborhoodGenerator + : public NeighborhoodGenerator { + public: + explicit RandomPrecedencesPackingNeighborhoodGenerator( + NeighborhoodGeneratorHelper const* helper, const std::string& name) + : NeighborhoodGenerator(name, helper) {} + + Neighborhood Generate(const CpSolverResponse& initial_solution, + double difficulty, absl::BitGenRef random) final; +}; + // Only make sense for problems with no_overlap_2d constraints. This select a // slice on one dimension, and fix the variables of all rectangles not strictly // included in this slice. diff --git a/ortools/sat/cp_model_solver.cc b/ortools/sat/cp_model_solver.cc index 9f5f323680e..62fab599bcd 100644 --- a/ortools/sat/cp_model_solver.cc +++ b/ortools/sat/cp_model_solver.cc @@ -23,14 +23,17 @@ #include #include #include -#include #include #include #include #include +#include "absl/container/flat_hash_map.h" +#include "absl/random/distributions.h" +#include "google/protobuf/arena.h" #include "ortools/base/logging.h" #include "ortools/base/timer.h" +#include "ortools/sat/intervals.h" #if !defined(__PORTABLE_PLATFORM__) #include "ortools/base/file.h" #include "ortools/base/helpers.h" @@ -3669,26 +3672,24 @@ void SolveCpModelParallel(const CpModelProto& model_proto, params, helper, &shared)); } - // TODO(user): If we have a model with scheduling + routing. We create - // a lot of LNS generators. Investigate if we can reduce this number. - if (!helper->TypeToConstraints(ConstraintProto::kNoOverlap).empty() || - !helper->TypeToConstraints(ConstraintProto::kNoOverlap2D).empty() || - !helper->TypeToConstraints(ConstraintProto::kCumulative).empty()) { + const bool has_no_overlap_or_cumulative = + !helper->TypeToConstraints(ConstraintProto::kNoOverlap).empty() || + !helper->TypeToConstraints(ConstraintProto::kCumulative).empty(); + const bool has_no_overlap2d = + !helper->TypeToConstraints(ConstraintProto::kNoOverlap2D).empty(); + + // Scheduling (no_overlap and cumulative) specific LNS. + const std::vector> intervals_in_constraints = + helper->GetUniqueIntervalSets(); + if (has_no_overlap_or_cumulative) { subsolvers.push_back(std::make_unique( std::make_unique( helper, "scheduling_intervals_lns"), params, helper, &shared)); - subsolvers.push_back(std::make_unique( - std::make_unique( - helper, "scheduling_precedences_lns"), - params, helper, &shared)); subsolvers.push_back(std::make_unique( std::make_unique( helper, "scheduling_time_window_lns"), params, helper, &shared)); - - const std::vector> intervals_in_constraints = - helper->GetUniqueIntervalSets(); if (intervals_in_constraints.size() > 2) { subsolvers.push_back(std::make_unique( std::make_unique( @@ -3696,16 +3697,30 @@ void SolveCpModelParallel(const CpModelProto& model_proto, "scheduling_resource_windows_lns"), params, helper, &shared)); } - if (!helper->TypeToConstraints(ConstraintProto::kNoOverlap2D).empty()) { - subsolvers.push_back(std::make_unique( - std::make_unique( - helper, "packing_rectangles_lns"), - params, helper, &shared)); - subsolvers.push_back(std::make_unique( - std::make_unique( - helper, "packing_slice_lns"), - params, helper, &shared)); - } + } + + // Packing (no_overlap_2d) Specific LNS. + if (has_no_overlap2d) { + subsolvers.push_back(std::make_unique( + std::make_unique( + helper, "packing_rectangles_lns"), + params, helper, &shared)); + subsolvers.push_back(std::make_unique( + std::make_unique( + helper, "packing_precedences_lns"), + params, helper, &shared)); + subsolvers.push_back(std::make_unique( + std::make_unique( + helper, "packing_slice_lns"), + params, helper, &shared)); + } + + // Generic scheduling/packing LNS. + if (has_no_overlap_or_cumulative || has_no_overlap2d) { + subsolvers.push_back(std::make_unique( + std::make_unique( + helper, "scheduling_precedences_lns"), + params, helper, &shared)); } const int num_circuit = static_cast(