29#include "absl/algorithm/container.h"
30#include "absl/base/log_severity.h"
31#include "absl/container/flat_hash_map.h"
32#include "absl/container/flat_hash_set.h"
33#include "absl/flags/flag.h"
34#include "absl/log/check.h"
35#include "absl/meta/type_traits.h"
36#include "absl/random/bit_gen_ref.h"
37#include "absl/random/distributions.h"
38#include "absl/strings/str_cat.h"
39#include "absl/strings/str_join.h"
40#include "absl/synchronization/mutex.h"
41#include "absl/types/span.h"
42#include "google/protobuf/arena.h"
46#include "ortools/sat/cp_model.pb.h"
57#include "ortools/sat/sat_parameters.pb.h"
73 CpModelProto
const* model_proto, SatParameters
const* parameters,
76 parameters_(*parameters),
77 model_proto_(*model_proto),
78 shared_bounds_(shared_bounds),
82 model_proto_.variables_size(),
84 local_arena_ = std::make_unique<google::protobuf::Arena>(
85 local_arena_storage_.data(), local_arena_storage_.size());
86 simplified_model_proto_ =
87 google::protobuf::Arena::Create<CpModelProto>(local_arena_.get());
89 CHECK(shared_response_ !=
nullptr);
90 if (shared_bounds_ !=
nullptr) {
91 shared_bounds_id_ = shared_bounds_->RegisterNewId();
93 *model_proto_with_only_variables_.mutable_variables() =
94 model_proto_.variables();
95 InitializeHelperData();
96 RecomputeHelperData();
101 if (shared_bounds_ !=
nullptr) {
102 std::vector<int> model_variables;
103 std::vector<int64_t> new_lower_bounds;
104 std::vector<int64_t> new_upper_bounds;
105 shared_bounds_->GetChangedBounds(shared_bounds_id_, &model_variables,
106 &new_lower_bounds, &new_upper_bounds);
108 bool new_variables_have_been_fixed =
false;
110 if (!model_variables.empty()) {
111 absl::MutexLock domain_lock(&domain_mutex_);
113 for (
int i = 0;
i < model_variables.size(); ++
i) {
114 const int var = model_variables[
i];
115 const int64_t new_lb = new_lower_bounds[
i];
116 const int64_t new_ub = new_upper_bounds[
i];
119 model_proto_with_only_variables_.variables(var).domain();
120 const int64_t old_lb = domain.Get(0);
121 const int64_t old_ub = domain.Get(domain.size() - 1);
122 VLOG(3) <<
"Variable: " << var <<
" old domain: [" << old_lb <<
", "
123 << old_ub <<
"] new domain: [" << new_lb <<
", " << new_ub
127 model_proto_with_only_variables_.variables(var));
148 model_proto_with_only_variables_.mutable_variables(var));
149 new_variables_have_been_fixed |= new_domain.
IsFixed();
154 if (new_variables_have_been_fixed) {
155 RecomputeHelperData();
160bool NeighborhoodGeneratorHelper::ObjectiveDomainIsConstraining()
const {
161 if (!model_proto_.has_objective())
return false;
162 if (model_proto_.objective().domain().empty())
return false;
164 int64_t min_activity = 0;
165 int64_t max_activity = 0;
166 const int num_terms = model_proto_.objective().vars().size();
167 for (
int i = 0;
i < num_terms; ++
i) {
168 const int var =
PositiveRef(model_proto_.objective().vars(
i));
169 const int64_t coeff = model_proto_.objective().coeffs(
i);
170 const auto& var_domain =
171 model_proto_with_only_variables_.variables(var).domain();
172 const int64_t v1 = coeff * var_domain[0];
173 const int64_t v2 = coeff * var_domain[var_domain.size() - 1];
174 min_activity += std::min(v1, v2);
175 max_activity += std::max(v1, v2);
179 const Domain inferred_domain =
180 Domain(min_activity, max_activity)
182 Domain(std::numeric_limits<int64_t>::min(), obj_domain.
Max()));
186void NeighborhoodGeneratorHelper::InitializeHelperData() {
187 type_to_constraints_.clear();
188 const int num_constraints = model_proto_.constraints_size();
189 for (
int c = 0; c < num_constraints; ++c) {
190 const int type = model_proto_.constraints(c).constraint_case();
191 if (
type >= type_to_constraints_.size()) {
192 type_to_constraints_.resize(
type + 1);
194 type_to_constraints_[
type].push_back(c);
197 const int num_variables = model_proto_.variables().size();
198 is_in_objective_.resize(num_variables,
false);
199 if (model_proto_.has_objective()) {
200 for (
const int ref : model_proto_.objective().vars()) {
208void NeighborhoodGeneratorHelper::RecomputeHelperData() {
210 absl::ReaderMutexLock domain_lock(&domain_mutex_);
224 CpModelProto mapping_proto;
228 int64_t new_size = local_arena_->SpaceUsed();
229 new_size += new_size / 2;
230 simplified_model_proto_->Clear();
231 local_arena_.reset();
232 local_arena_storage_.resize(new_size);
233 local_arena_ = std::make_unique<google::protobuf::Arena>(
234 local_arena_storage_.data(), local_arena_storage_.size());
235 simplified_model_proto_ =
236 google::protobuf::Arena::Create<CpModelProto>(local_arena_.get());
237 *simplified_model_proto_->mutable_variables() =
238 model_proto_with_only_variables_.variables();
239 PresolveContext context(&local_model, simplified_model_proto_,
241 ModelCopy copier(&context);
245 copier.ImportAndSimplifyConstraints(model_proto_, {});
251 const auto& constraints = simplified_model_proto_->constraints();
252 constraint_to_var_.clear();
253 constraint_to_var_.reserve(constraints.size());
254 for (
int ct_index = 0; ct_index < constraints.size(); ++ct_index) {
260 if (constraints[ct_index].constraint_case() == ConstraintProto::kInterval) {
266 if (IsConstant(var))
continue;
267 tmp_row_.push_back(var);
272 bool need_sort =
false;
273 for (
const int interval :
UsedIntervals(constraints[ct_index])) {
276 if (IsConstant(var))
continue;
277 tmp_row_.push_back(var);
283 if (tmp_row_.size() <= 1) {
291 constraint_to_var_.Add(tmp_row_);
296 var_to_constraint_.ResetFromTranspose(
298 model_proto_.variables().size());
302 active_variables_.clear();
303 const int num_variables = model_proto_.variables_size();
304 active_variables_set_.assign(num_variables,
false);
305 for (
int i = 0;
i < num_variables; ++
i) {
306 if (!IsConstant(
i)) {
307 active_variables_.push_back(
i);
308 active_variables_set_[
i] =
true;
312 active_objective_variables_.clear();
313 for (
const int var : model_proto_.objective().vars()) {
315 if (active_variables_set_[var]) {
316 active_objective_variables_.push_back(var);
322 DenseConnectedComponentsFinder union_find;
324 for (
int c = 0;
c < constraint_to_var_.size(); ++
c) {
325 const auto row = constraint_to_var_[
c];
326 if (row.size() <= 1)
continue;
327 for (
int i = 1;
i < row.size(); ++
i) {
334 if (ObjectiveDomainIsConstraining()) {
335 const auto& refs = model_proto_.objective().vars();
336 const int num_terms = refs.size();
337 for (
int i = 1;
i < num_terms; ++
i) {
348 var_to_component_index_.assign(num_variables, -1);
349 for (
int var = 0; var < num_variables; ++var) {
350 if (IsConstant(var))
continue;
351 const int root = union_find.
FindRoot(var);
352 DCHECK_LT(root, var_to_component_index_.size());
353 int& index = var_to_component_index_[root];
355 index = components_.size();
356 components_.push_back({});
358 var_to_component_index_[var] = index;
359 components_[index].push_back(var);
366 if (!shared_response_->LoggingIsEnabled())
return;
368 std::vector<int> component_sizes;
369 for (
const std::vector<int>& component : components_) {
370 component_sizes.push_back(component.size());
372 std::sort(component_sizes.begin(), component_sizes.end(),
373 std::greater<int>());
374 std::string compo_message;
375 if (component_sizes.size() > 1) {
376 if (component_sizes.size() <= 10) {
378 absl::StrCat(
" compo:", absl::StrJoin(component_sizes,
","));
380 component_sizes.resize(10);
382 absl::StrCat(
" compo:", absl::StrJoin(component_sizes,
","),
",...");
389 shared_response_->LogMessageWithThrottling(
390 "Model", absl::StrCat(
"var:", active_variables_.size(),
"/",
391 num_variables,
" constraints:",
392 simplified_model_proto_->constraints().size(),
"/",
393 model_proto_.constraints().size(), compo_message));
397 return active_variables_set_[var];
400bool NeighborhoodGeneratorHelper::IsConstant(
int var)
const {
401 const auto& var_proto = model_proto_with_only_variables_.variables(var);
402 return var_proto.domain_size() == 2 &&
403 var_proto.domain(0) == var_proto.domain(1);
411 absl::ReaderMutexLock lock(&domain_mutex_);
412 *neighborhood.
delta.mutable_variables() =
413 model_proto_with_only_variables_.variables();
424bool NeighborhoodGeneratorHelper::IntervalIsActive(
425 int index,
const CpSolverResponse& initial_solution)
const {
426 const ConstraintProto& interval_ct =
ModelProto().constraints(index);
430 if (interval_ct.enforcement_literal().size() == 1) {
431 const int enforcement_ref = interval_ct.enforcement_literal(0);
432 const int enforcement_var =
PositiveRef(enforcement_ref);
433 const int value = initial_solution.solution(enforcement_var);
434 if (
RefIsPositive(enforcement_ref) == (value == 0))
return false;
437 for (
const int v : interval_ct.interval().start().vars()) {
438 if (!IsConstant(v))
return true;
440 for (
const int v : interval_ct.interval().size().vars()) {
441 if (!IsConstant(v))
return true;
443 for (
const int v : interval_ct.interval().end().vars()) {
444 if (!IsConstant(v))
return true;
450 absl::Span<const int> unfiltered_intervals,
451 const CpSolverResponse& initial_solution)
const {
452 std::vector<int> filtered_intervals;
453 filtered_intervals.reserve(unfiltered_intervals.size());
454 absl::ReaderMutexLock lock(&domain_mutex_);
455 for (
const int i : unfiltered_intervals) {
456 if (IntervalIsActive(
i, initial_solution)) filtered_intervals.push_back(
i);
458 return filtered_intervals;
462 const CpSolverResponse& initial_solution)
const {
467std::vector<NeighborhoodGeneratorHelper::ActiveRectangle>
469 const CpSolverResponse& initial_solution)
const {
470 const std::vector<int> active_intervals =
472 const absl::flat_hash_set<int> active_intervals_set(active_intervals.begin(),
473 active_intervals.end());
475 absl::flat_hash_map<std::pair<int, int>, std::vector<int>> active_rectangles;
477 const NoOverlap2DConstraintProto& ct =
478 model_proto_.constraints(ct_index).no_overlap_2d();
479 for (
int i = 0;
i < ct.x_intervals_size(); ++
i) {
480 const int x_i = ct.x_intervals(
i);
481 const int y_i = ct.y_intervals(
i);
482 if (active_intervals_set.contains(x_i) ||
483 active_intervals_set.contains(y_i)) {
484 active_rectangles[{x_i, y_i}].push_back(ct_index);
489 std::vector<ActiveRectangle> results;
490 results.reserve(active_rectangles.size());
491 for (
const auto& [rectangle, no_overlap_2d_constraints] : active_rectangles) {
496 no_overlap_2d_constraints.end()};
501std::vector<std::vector<int>>
503 std::vector<std::vector<int>> intervals_in_constraints;
504 absl::flat_hash_set<std::vector<int>> added_intervals_sets;
505 const auto add_interval_list_only_once =
506 [&intervals_in_constraints,
507 &added_intervals_sets](
const auto& intervals) {
508 std::vector<int> candidate({intervals.begin(), intervals.end()});
510 if (added_intervals_sets.insert(candidate).second) {
511 intervals_in_constraints.push_back(candidate);
515 add_interval_list_only_once(
516 model_proto_.constraints(ct_index).no_overlap().intervals());
519 add_interval_list_only_once(
520 model_proto_.constraints(ct_index).cumulative().intervals());
523 add_interval_list_only_once(
524 model_proto_.constraints(ct_index).no_overlap_2d().x_intervals());
525 add_interval_list_only_once(
526 model_proto_.constraints(ct_index).no_overlap_2d().y_intervals());
528 return intervals_in_constraints;
533int64_t GetLinearExpressionValue(
const LinearExpressionProto& expr,
534 const CpSolverResponse& initial_solution) {
535 int64_t result = expr.offset();
536 for (
int i = 0;
i < expr.vars_size(); ++
i) {
537 result += expr.coeffs(
i) * initial_solution.solution(expr.vars(
i));
542void RestrictAffineExpression(
const LinearExpressionProto& expr,
543 const Domain& restriction,
544 CpModelProto* mutable_proto) {
545 CHECK_LE(expr.vars().size(), 1);
546 if (expr.vars().empty())
return;
547 const Domain implied_domain = restriction.AdditionWith(
Domain(-expr.offset()))
548 .InverseMultiplicationBy(expr.coeffs(0));
552 if (!domain.IsEmpty()) {
557struct StartEndIndex {
560 int index_in_input_vector;
562 bool operator<(
const StartEndIndex& o)
const {
563 return std::tie(start, end, noise, index_in_input_vector) <
564 std::tie(o.start, o.end, o.noise, o.index_in_input_vector);
568struct TimePartition {
569 std::vector<int> indices_before_selected;
570 std::vector<int> selected_indices;
571 std::vector<int> indices_after_selected;
576TimePartition PartitionIndicesAroundRandomTimeWindow(
577 absl::Span<const int> intervals,
const CpModelProto& model_proto,
578 const CpSolverResponse& initial_solution,
double difficulty,
579 absl::BitGenRef random) {
580 std::vector<StartEndIndex> start_end_indices;
581 for (
int index = 0; index < intervals.size(); ++index) {
582 const int interval = intervals[index];
583 const ConstraintProto& interval_ct = model_proto.constraints(interval);
584 const int64_t start_value = GetLinearExpressionValue(
585 interval_ct.interval().start(), initial_solution);
586 const int64_t end_value = GetLinearExpressionValue(
587 interval_ct.interval().end(), initial_solution);
588 start_end_indices.push_back(
589 {start_value, end_value, index, absl::Uniform(random, 0., 1.0)});
592 if (start_end_indices.empty())
return {};
594 std::sort(start_end_indices.begin(), start_end_indices.end());
595 const int relaxed_size = std::floor(difficulty * start_end_indices.size());
597 std::uniform_int_distribution<int> random_var(
598 0, start_end_indices.size() - relaxed_size - 1);
601 const int random_start_index = random_var(random);
608 std::sort(start_end_indices.begin() + random_start_index,
609 start_end_indices.end(),
610 [](
const StartEndIndex& a,
const StartEndIndex&
b) {
611 return std::tie(a.end, a.noise, a.index_in_input_vector) <
612 std::tie(b.end, b.noise, b.index_in_input_vector);
614 TimePartition result;
616 for (;
i < random_start_index; ++
i) {
617 result.indices_before_selected.push_back(
618 start_end_indices[
i].index_in_input_vector);
620 for (;
i < random_start_index + relaxed_size; ++
i) {
621 result.selected_indices.push_back(
622 start_end_indices[
i].index_in_input_vector);
624 for (;
i < start_end_indices.size(); ++
i) {
625 result.indices_after_selected.push_back(
626 start_end_indices[
i].index_in_input_vector);
641 bool operator<(
const Demand& other)
const {
642 return std::tie(start, height, end) <
643 std::tie(other.start, other.height, other.end);
646 std::string DebugString()
const {
647 return absl::StrCat(
"{i=", interval_index,
" span=[", start,
",", end,
"]",
652void InsertPrecedencesFromSortedListOfNonOverlapingIntervals(
653 const std::vector<Demand>& demands,
654 absl::flat_hash_set<std::pair<int, int>>* precedences) {
655 for (
int i = 0;
i + 1 < demands.size(); ++
i) {
656 DCHECK_LE(demands[
i].end, demands[
i + 1].start);
658 {demands[
i].interval_index, demands[
i + 1].interval_index});
662bool IsPresent(
const ConstraintProto& interval_ct,
663 const CpSolverResponse& initial_solution) {
664 if (interval_ct.enforcement_literal().size() != 1)
return true;
666 const int enforcement_ref = interval_ct.enforcement_literal(0);
667 const int enforcement_var =
PositiveRef(enforcement_ref);
668 const int64_t value = initial_solution.solution(enforcement_var);
672void InsertNoOverlapPrecedences(
673 const absl::flat_hash_set<int>& ignored_intervals,
674 const CpSolverResponse& initial_solution,
const CpModelProto& model_proto,
675 int no_overlap_index,
676 absl::flat_hash_set<std::pair<int, int>>* precedences) {
677 std::vector<Demand> demands;
678 const NoOverlapConstraintProto& no_overlap =
679 model_proto.constraints(no_overlap_index).no_overlap();
680 for (
const int interval_index : no_overlap.intervals()) {
681 if (ignored_intervals.contains(interval_index))
continue;
682 const ConstraintProto& interval_ct =
683 model_proto.constraints(interval_index);
684 if (!IsPresent(interval_ct, initial_solution))
continue;
686 const int64_t start_value = GetLinearExpressionValue(
687 interval_ct.interval().start(), initial_solution);
688 const int64_t end_value = GetLinearExpressionValue(
689 interval_ct.interval().end(), initial_solution);
690 DCHECK_LE(start_value, end_value);
691 demands.push_back({interval_index, start_value, end_value, 1});
696 std::sort(demands.begin(), demands.end());
697 InsertPrecedencesFromSortedListOfNonOverlapingIntervals(demands, precedences);
700void ProcessDemandListFromCumulativeConstraint(
701 const std::vector<Demand>& demands, int64_t capacity,
702 std::deque<std::pair<std::vector<Demand>, int64_t>>* to_process,
703 absl::BitGenRef random,
704 absl::flat_hash_set<std::pair<int, int>>* precedences) {
705 if (demands.size() <= 1)
return;
708 int64_t sum_of_min_two_capacities = 2;
710 int64_t min1 = std::numeric_limits<int64_t>::max();
711 int64_t min2 = std::numeric_limits<int64_t>::max();
712 for (
const Demand& demand : demands) {
713 if (demand.height <= min1) {
715 min1 = demand.height;
716 }
else if (demand.height < min2) {
717 min2 = demand.height;
720 sum_of_min_two_capacities = min1 + min2;
723 DCHECK_GT(sum_of_min_two_capacities, 1);
724 if (sum_of_min_two_capacities > capacity) {
725 InsertPrecedencesFromSortedListOfNonOverlapingIntervals(demands,
730 std::vector<int64_t> unique_starts;
731 for (
const Demand& demand : demands) {
732 DCHECK(unique_starts.empty() || demand.start >= unique_starts.back());
733 if (unique_starts.empty() || unique_starts.back() < demand.start) {
734 unique_starts.push_back(demand.start);
737 DCHECK(std::is_sorted(unique_starts.begin(), unique_starts.end()));
738 const int num_points = unique_starts.size();
741 const int64_t capacity1 = capacity / 2;
742 std::vector<int64_t> usage1(num_points);
743 std::vector<Demand> demands1;
745 const int64_t capacity2 = capacity - capacity1;
746 std::vector<int64_t> usage2(num_points);
747 std::vector<Demand> demands2;
750 for (
const Demand& d : demands) {
753 while (usage_index < num_points && unique_starts[usage_index] < d.start) {
756 DCHECK_LT(usage_index, num_points);
757 DCHECK_EQ(unique_starts[usage_index], d.start);
758 const int64_t slack1 = capacity1 - usage1[usage_index];
759 const int64_t slack2 = capacity2 - usage2[usage_index];
766 ? absl::Bernoulli(random, 0.5)
767 : (d.height <= std::min(slack1, slack2) ? slack2 < slack1
770 auto& selected_usage = prefer2 ? usage2 : usage1;
771 auto& residual_usage = prefer2 ? usage1 : usage2;
772 std::vector<Demand>& selected_demands = prefer2 ? demands2 : demands1;
773 std::vector<Demand>& residual_demands = prefer2 ? demands1 : demands2;
774 const int64_t selected_slack = prefer2 ? slack2 : slack1;
776 const int64_t assigned_to_selected = std::min(selected_slack, d.height);
777 DCHECK_GT(assigned_to_selected, 0);
778 for (
int i = usage_index;
i < num_points; ++
i) {
779 if (d.end <= unique_starts[
i])
break;
780 selected_usage[
i] += assigned_to_selected;
782 selected_demands.push_back(
783 {d.interval_index, d.start, d.end, assigned_to_selected});
785 if (d.height > selected_slack) {
786 const int64_t residual = d.height - selected_slack;
787 DCHECK_GT(residual, 0);
788 DCHECK_LE(residual, prefer2 ? slack1 : slack2);
789 for (
int i = usage_index;
i < num_points; ++
i) {
790 if (d.end <= unique_starts[
i])
break;
791 residual_usage[
i] += residual;
793 residual_demands.push_back({d.interval_index, d.start, d.end, residual});
797 if (demands1.size() > 1) {
798 to_process->emplace_back(std::move(demands1), capacity1);
800 if (demands2.size() > 1) {
801 to_process->emplace_back(std::move(demands2), capacity2);
805void InsertCumulativePrecedences(
806 const absl::flat_hash_set<int>& ignored_intervals,
807 const CpSolverResponse& initial_solution,
const CpModelProto& model_proto,
808 int cumulative_index, absl::BitGenRef random,
809 absl::flat_hash_set<std::pair<int, int>>* precedences) {
810 const CumulativeConstraintProto& cumulative =
811 model_proto.constraints(cumulative_index).cumulative();
813 std::vector<Demand> demands;
814 for (
int i = 0;
i < cumulative.intervals().size(); ++
i) {
815 const int interval_index = cumulative.intervals(
i);
816 if (ignored_intervals.contains(interval_index))
continue;
817 const ConstraintProto& interval_ct =
818 model_proto.constraints(interval_index);
819 if (!IsPresent(interval_ct, initial_solution))
continue;
821 const int64_t start_value = GetLinearExpressionValue(
822 interval_ct.interval().start(), initial_solution);
823 const int64_t end_value = GetLinearExpressionValue(
824 interval_ct.interval().end(), initial_solution);
825 const int64_t demand_value =
826 GetLinearExpressionValue(cumulative.demands(
i), initial_solution);
827 if (start_value == end_value || demand_value == 0)
continue;
829 demands.push_back({interval_index, start_value, end_value, demand_value});
831 std::sort(demands.begin(), demands.end());
833 const int64_t capacity_value =
834 GetLinearExpressionValue(cumulative.capacity(), initial_solution);
835 DCHECK_GT(capacity_value, 0);
838 std::deque<std::pair<std::vector<Demand>, int64_t>> to_process;
839 to_process.emplace_back(std::move(demands), capacity_value);
841 while (!to_process.empty()) {
842 auto& next_task = to_process.front();
843 ProcessDemandListFromCumulativeConstraint(next_task.first,
845 &to_process, random, precedences);
846 to_process.pop_front();
850struct IndexedRectangle {
854 bool operator<(
const IndexedRectangle& other)
const {
855 return std::tie(r.x_min, r.x_max) < std::tie(other.r.x_min, other.r.x_max);
859void InsertRectanglePredecences(
860 absl::Span<const IndexedRectangle> rectangles,
861 absl::flat_hash_set<std::pair<int, int>>* precedences) {
863 std::vector<IntegerValue> interesting_points;
864 for (
const IndexedRectangle& idx_r : rectangles) {
865 interesting_points.push_back(idx_r.r.y_max - 1);
868 std::vector<Demand> demands;
869 for (
const IntegerValue t : interesting_points) {
871 for (
const IndexedRectangle& idx_r : rectangles) {
872 if (idx_r.r.y_min > t || idx_r.r.y_max <= t)
continue;
873 demands.push_back({idx_r.interval_index, idx_r.r.x_min.value(),
874 idx_r.r.x_max.value(), 1});
876 std::sort(demands.begin(), demands.end());
877 InsertPrecedencesFromSortedListOfNonOverlapingIntervals(demands,
882void InsertNoOverlap2dPrecedences(
883 const absl::flat_hash_set<int>& ignored_intervals,
884 const CpSolverResponse& initial_solution,
const CpModelProto& model_proto,
885 int no_overlap_2d_index,
886 absl::flat_hash_set<std::pair<int, int>>* precedences) {
887 std::vector<Demand> demands;
888 const NoOverlap2DConstraintProto& no_overlap_2d =
889 model_proto.constraints(no_overlap_2d_index).no_overlap_2d();
890 std::vector<IndexedRectangle> x_main;
891 std::vector<IndexedRectangle> y_main;
892 for (
int i = 0;
i < no_overlap_2d.x_intervals_size(); ++
i) {
894 const int x_interval_index = no_overlap_2d.x_intervals(
i);
895 if (ignored_intervals.contains(x_interval_index))
continue;
896 const ConstraintProto& x_interval_ct =
897 model_proto.constraints(x_interval_index);
898 if (!IsPresent(x_interval_ct, initial_solution))
continue;
900 const int y_interval_index = no_overlap_2d.y_intervals(
i);
901 if (ignored_intervals.contains(y_interval_index))
continue;
902 const ConstraintProto& y_interval_ct =
903 model_proto.constraints(y_interval_index);
904 if (!IsPresent(y_interval_ct, initial_solution))
continue;
906 const int64_t x_start_value = GetLinearExpressionValue(
907 x_interval_ct.interval().start(), initial_solution);
908 const int64_t x_end_value = GetLinearExpressionValue(
909 x_interval_ct.interval().end(), initial_solution);
910 const int64_t y_start_value = GetLinearExpressionValue(
911 y_interval_ct.interval().start(), initial_solution);
912 const int64_t y_end_value = GetLinearExpressionValue(
913 y_interval_ct.interval().end(), initial_solution);
916 if (x_start_value == x_end_value || y_start_value == y_end_value)
continue;
918 x_main.push_back({.interval_index = x_interval_index,
919 .r = {.x_min = x_start_value,
920 .x_max = x_end_value,
921 .y_min = y_start_value,
922 .y_max = y_end_value}});
923 y_main.push_back({.interval_index = y_interval_index,
924 .r = {.x_min = y_start_value,
925 .x_max = y_end_value,
926 .y_min = x_start_value,
927 .y_max = x_end_value}});
930 if (x_main.empty() || y_main.empty())
return;
932 std::sort(x_main.begin(), x_main.end());
933 InsertRectanglePredecences(x_main, precedences);
934 std::sort(y_main.begin(), y_main.end());
935 InsertRectanglePredecences(y_main, precedences);
943std::vector<std::pair<int, int>>
945 const absl::flat_hash_set<int>& ignored_intervals,
946 const CpSolverResponse& initial_solution, absl::BitGenRef random)
const {
947 absl::flat_hash_set<std::pair<int, int>> precedences;
949 InsertNoOverlapPrecedences(ignored_intervals, initial_solution,
953 InsertCumulativePrecedences(ignored_intervals, initial_solution,
957 InsertNoOverlap2dPrecedences(ignored_intervals, initial_solution,
962 std::vector<std::pair<int, int>> result(precedences.begin(),
964 std::sort(result.begin(), result.end());
968std::vector<std::vector<int>>
970 const CpSolverResponse& initial_solution)
const {
971 struct HeadAndArcBooleanVariable {
976 std::vector<std::vector<int>> result;
977 absl::flat_hash_map<int, HeadAndArcBooleanVariable>
978 tail_to_head_and_arc_bool_var;
981 const CircuitConstraintProto& ct =
ModelProto().constraints(
i).circuit();
984 int min_node = std::numeric_limits<int>::max();
985 tail_to_head_and_arc_bool_var.clear();
986 for (
int i = 0;
i < ct.literals_size(); ++
i) {
987 const int literal = ct.literals(
i);
988 const int head = ct.heads(
i);
989 const int tail = ct.tails(
i);
991 const int64_t value = initial_solution.solution(bool_var);
995 if (head == tail)
continue;
996 tail_to_head_and_arc_bool_var[tail] = {head, bool_var};
997 min_node = std::min(tail, min_node);
999 if (tail_to_head_and_arc_bool_var.empty())
continue;
1002 int current_node = min_node;
1003 std::vector<int> path;
1005 auto it = tail_to_head_and_arc_bool_var.find(current_node);
1006 CHECK(it != tail_to_head_and_arc_bool_var.end());
1007 current_node = it->second.head;
1008 path.push_back(it->second.bool_var);
1009 }
while (current_node != min_node);
1010 result.push_back(std::move(path));
1013 std::vector<HeadAndArcBooleanVariable> route_starts;
1015 const RoutesConstraintProto& ct =
ModelProto().constraints(
i).routes();
1016 tail_to_head_and_arc_bool_var.clear();
1017 route_starts.clear();
1020 for (
int i = 0;
i < ct.literals_size(); ++
i) {
1021 const int literal = ct.literals(
i);
1022 const int head = ct.heads(
i);
1023 const int tail = ct.tails(
i);
1025 const int64_t value = initial_solution.solution(bool_var);
1029 if (head == tail)
continue;
1031 route_starts.push_back({head, bool_var});
1033 tail_to_head_and_arc_bool_var[tail] = {head, bool_var};
1038 for (
const HeadAndArcBooleanVariable& head_var : route_starts) {
1039 std::vector<int> path;
1040 int current_node = head_var.head;
1041 path.push_back(head_var.bool_var);
1043 auto it = tail_to_head_and_arc_bool_var.find(current_node);
1044 CHECK(it != tail_to_head_and_arc_bool_var.end());
1045 current_node = it->second.head;
1046 path.push_back(it->second.bool_var);
1047 }
while (current_node != 0);
1048 result.push_back(std::move(path));
1056 const CpSolverResponse& base_solution,
1058 const int num_variables = variables_to_fix.
size();
1060 neighborhood.
delta.mutable_variables()->Reserve(num_variables);
1064 const int unique_objective_variable =
1065 model_proto_.has_objective() && model_proto_.objective().vars_size() == 1
1066 ? model_proto_.objective().vars(0)
1072 absl::ReaderMutexLock domain_lock(&domain_mutex_);
1073 for (
int i = 0;
i < num_variables; ++
i) {
1074 const IntegerVariableProto& current_var =
1075 model_proto_with_only_variables_.variables(
i);
1076 IntegerVariableProto* new_var = neighborhood.
delta.add_variables();
1079 if (
DEBUG_MODE) new_var->set_name(current_var.name());
1081 if (variables_to_fix[
i] &&
i != unique_objective_variable) {
1086 const int64_t base_value = base_solution.solution(
i);
1088 new_var->add_domain(base_value);
1089 new_var->add_domain(base_value);
1095 int64_t closest_value = domain.
Min();
1096 int64_t closest_dist = std::abs(closest_value - base_value);
1098 for (
const int64_t value : {interval.start, interval.end}) {
1099 const int64_t dist = std::abs(value - base_value);
1100 if (dist < closest_dist) {
1101 closest_value = value;
1102 closest_dist = dist;
1109 *new_var->mutable_domain() = current_var.domain();
1119 std::vector<int> count(components_.size(), 0);
1120 const int num_variables = neighborhood.
delta.variables().size();
1121 for (
int var = 0; var < num_variables; ++var) {
1122 const auto& domain = neighborhood.
delta.variables(var).domain();
1123 if (domain.size() != 2 || domain[0] != domain[1]) {
1125 if (is_in_objective_[var]) {
1128 const int c = var_to_component_index_[var];
1129 if (c != -1) count[c]++;
1133 for (
int i = 0;
i < components_.size(); ++
i) {
1134 if (count[
i] == components_[
i].size()) {
1137 components_[
i].begin(), components_[
i].end());
1147 if (model_proto_.has_objective() &&
1148 (model_proto_.objective().domain().size() != 2 ||
1149 shared_response_->GetInnerObjectiveLowerBound() <
1150 model_proto_.objective().domain(0))) {
1154 const int num_relaxed = num_variables - num_fixed;
1155 neighborhood.
delta.mutable_solution_hint()->mutable_vars()->Reserve(
1157 neighborhood.
delta.mutable_solution_hint()->mutable_values()->Reserve(
1169 return neighborhood;
1173 const CpSolverResponse& initial_solution, CpModelProto* model_proto)
const {
1175 model_proto->clear_solution_hint();
1176 const auto is_fixed = [model_proto](
int var) {
1177 const IntegerVariableProto& var_proto = model_proto->variables(var);
1178 return var_proto.domain_size() == 2 &&
1179 var_proto.domain(0) == var_proto.domain(1);
1181 for (
int var = 0; var < model_proto->variables_size(); ++var) {
1182 if (is_fixed(var))
continue;
1184 model_proto->mutable_solution_hint()->add_vars(var);
1185 model_proto->mutable_solution_hint()->add_values(
1186 initial_solution.solution(var));
1191 const CpSolverResponse& initial_solution,
1192 absl::Span<const int> relaxed_variables)
const {
1196 for (
const int i : active_variables_) {
1197 fixed_variables.
Set(
i);
1200 for (
const int var : relaxed_variables) fixed_variables.
Clear(var);
1205 const CpSolverResponse& initial_solution)
const {
1209 for (
const int i : active_variables_) {
1210 fixed_variables.
Set(
i);
1217 CpModelProto updated_model = model_proto_;
1219 absl::MutexLock domain_lock(&domain_mutex_);
1220 *updated_model.mutable_variables() =
1221 model_proto_with_only_variables_.variables();
1223 return updated_model;
1227 return (
helper_.shared_response().SolutionsRepository().NumSolutions() > 0);
1232 DCHECK_GE(total_num_calls, num_calls_);
1233 if (num_calls_ <= 10)
return std::numeric_limits<double>::infinity();
1234 return current_average_ + sqrt((2 * log(total_num_calls)) / num_calls_);
1242 std::sort(solve_data_.begin(), solve_data_.end());
1245 int num_fully_solved_in_batch = 0;
1246 int num_not_fully_solved_in_batch = 0;
1248 double total_dtime = 0.0;
1249 for (
const SolveData& data : solve_data_) {
1255 if (data.status == CpSolverStatus::INFEASIBLE ||
1256 data.status == CpSolverStatus::OPTIMAL) {
1257 ++num_fully_solved_calls_;
1258 ++num_fully_solved_in_batch;
1260 ++num_not_fully_solved_in_batch;
1269 const IntegerValue best_objective_improvement = IntegerValue(
CapSub(
1270 data.initial_best_objective.value(), data.new_objective.value()));
1271 if (best_objective_improvement > 0) {
1272 num_consecutive_non_improving_calls_ = 0;
1273 next_time_limit_bump_ = 50;
1275 ++num_consecutive_non_improving_calls_;
1279 if (data.base_objective > data.new_objective) {
1280 ++num_improving_calls_;
1285 const double gain_per_time_unit =
1286 std::max(0.0,
static_cast<double>(best_objective_improvement.value())) /
1287 (1.0 + data.deterministic_time);
1288 if (num_calls_ <= 100) {
1289 current_average_ += (gain_per_time_unit - current_average_) / num_calls_;
1291 current_average_ = 0.9 * current_average_ + 0.1 * gain_per_time_unit;
1294 total_dtime += data.deterministic_time;
1298 difficulty_.Update(num_not_fully_solved_in_batch,
1299 num_fully_solved_in_batch);
1307 if (num_consecutive_non_improving_calls_ > next_time_limit_bump_) {
1308 next_time_limit_bump_ = num_consecutive_non_improving_calls_ + 50;
1316 solve_data_.clear();
1323void GetRandomSubset(
double relative_size, std::vector<T>*
base,
1324 absl::BitGenRef random) {
1325 if (
base->empty())
return;
1329 std::shuffle(
base->begin(),
base->end(), random);
1330 const int target_size = std::round(relative_size *
base->size());
1331 base->resize(target_size);
1337 const CpSolverResponse& initial_solution,
SolveData& data,
1338 absl::BitGenRef random) {
1339 std::vector<int> fixed_variables =
helper_.ActiveVariables();
1340 GetRandomSubset(1.0 - data.
difficulty, &fixed_variables, random);
1343 for (
const int var : fixed_variables) to_fix.
Set(var);
1344 return helper_.FixGivenVariables(initial_solution, to_fix);
1348 const CpSolverResponse& initial_solution,
SolveData& data,
1349 absl::BitGenRef random) {
1351 return helper_.FullNeighborhood();
1354 std::vector<int> relaxed_variables;
1356 absl::ReaderMutexLock graph_lock(&
helper_.graph_mutex_);
1357 const int num_active_constraints =
helper_.ConstraintToVar().size();
1358 std::vector<int> active_constraints(num_active_constraints);
1359 for (
int c = 0; c < num_active_constraints; ++c) {
1360 active_constraints[c] = c;
1362 std::shuffle(active_constraints.begin(), active_constraints.end(), random);
1364 const int num_model_vars =
helper_.ModelProto().variables_size();
1365 std::vector<bool> visited_variables_set(num_model_vars,
false);
1367 const int num_active_vars =
1368 helper_.ActiveVariablesWhileHoldingLock().size();
1369 const int target_size = std::ceil(data.
difficulty * num_active_vars);
1370 if (target_size == num_active_vars)
return helper_.FullNeighborhood();
1373 for (
const int constraint_index : active_constraints) {
1376 for (
const int var :
helper_.ConstraintToVar()[constraint_index]) {
1377 if (visited_variables_set[var])
continue;
1378 visited_variables_set[var] =
true;
1380 relaxed_variables.push_back(var);
1381 if (relaxed_variables.size() >= target_size)
break;
1384 if (relaxed_variables.size() >= target_size)
break;
1388 return helper_.RelaxGivenVariables(initial_solution, relaxed_variables);
1394 const CpSolverResponse& initial_solution,
SolveData& data,
1395 absl::BitGenRef random) {
1396 const int num_model_vars =
helper_.ModelProto().variables_size();
1397 std::vector<bool> visited_variables_set(num_model_vars,
false);
1398 std::vector<int> relaxed_variables;
1399 std::vector<int> visited_variables;
1402 const int num_model_constraints =
helper_.ModelProto().constraints_size();
1403 std::vector<bool> scanned_constraints(num_model_constraints,
false);
1405 std::vector<int> random_variables;
1407 absl::ReaderMutexLock graph_lock(&
helper_.graph_mutex_);
1411 const int num_active_vars =
1412 helper_.ActiveVariablesWhileHoldingLock().size();
1413 const int num_objective_variables =
1414 helper_.ActiveObjectiveVariablesWhileHoldingLock().size();
1415 const int target_size = std::ceil(data.
difficulty * num_active_vars);
1416 if (target_size == num_active_vars)
return helper_.FullNeighborhood();
1418 const int first_var =
1419 num_objective_variables > 0
1420 ?
helper_.ActiveObjectiveVariablesWhileHoldingLock()
1421 [absl::Uniform<int>(random, 0, num_objective_variables)]
1422 :
helper_.ActiveVariablesWhileHoldingLock()[absl::Uniform<int>(
1423 random, 0, num_active_vars)];
1424 visited_variables_set[first_var] =
true;
1425 visited_variables.push_back(first_var);
1426 relaxed_variables.push_back(first_var);
1428 for (
int i = 0;
i < visited_variables.size(); ++
i) {
1429 random_variables.clear();
1432 for (
const int ct :
helper_.VarToConstraint()[visited_variables[
i]]) {
1433 if (scanned_constraints[ct])
continue;
1434 scanned_constraints[ct] =
true;
1435 for (
const int var :
helper_.ConstraintToVar()[ct]) {
1436 if (visited_variables_set[var])
continue;
1437 visited_variables_set[var] =
true;
1438 random_variables.push_back(var);
1443 std::shuffle(random_variables.begin(), random_variables.end(), random);
1444 for (
const int var : random_variables) {
1445 if (relaxed_variables.size() < target_size) {
1446 visited_variables.push_back(var);
1448 relaxed_variables.push_back(var);
1454 if (relaxed_variables.size() >= target_size)
break;
1458 return helper_.RelaxGivenVariables(initial_solution, relaxed_variables);
1464 const CpSolverResponse& initial_solution,
SolveData& data,
1465 absl::BitGenRef random) {
1466 const int num_model_vars =
helper_.ModelProto().variables_size();
1467 if (num_model_vars == 0)
return helper_.NoNeighborhood();
1475 int num_active_vars = 0;
1476 std::vector<int> active_objective_vars;
1478 absl::ReaderMutexLock graph_lock(&
helper_.graph_mutex_);
1479 num_active_vars =
helper_.ActiveVariablesWhileHoldingLock().size();
1480 active_objective_vars =
helper_.ActiveObjectiveVariablesWhileHoldingLock();
1481 constraints_to_vars =
helper_.ConstraintToVar();
1482 vars_to_constraints =
helper_.VarToConstraint();
1485 const int target_size = std::ceil(data.
difficulty * num_active_vars);
1486 if (target_size == 0)
return helper_.NoNeighborhood();
1489 const int num_objective_variables = active_objective_vars.size();
1490 if (num_objective_variables == 0)
return helper_.NoNeighborhood();
1491 const int first_var = active_objective_vars[absl::Uniform<int>(
1492 random, 0, num_objective_variables)];
1494 std::vector<bool> relaxed_variables_set(num_model_vars,
false);
1495 std::vector<int> relaxed_variables;
1497 std::vector<int> active_vars;
1499 relaxed_variables_set[first_var] =
true;
1500 relaxed_variables.push_back(first_var);
1501 active_vars.push_back(first_var);
1503 while (relaxed_variables.size() < target_size) {
1504 if (active_vars.empty())
break;
1506 const int tail_index = absl::Uniform<int>(random, 0, active_vars.size());
1507 const int tail_var = active_vars[tail_index];
1508 int head_var = tail_var;
1509 while (!vars_to_constraints[tail_var].empty() && head_var == tail_var) {
1510 const auto cts = vars_to_constraints[tail_var];
1511 const int pos_ct = absl::Uniform<int>(random, 0, cts.size());
1512 const int ct = cts[pos_ct];
1513 while (!constraints_to_vars[ct].empty() && head_var == tail_var) {
1514 const auto vars = constraints_to_vars[ct];
1515 const int pos_var = absl::Uniform<int>(random, 0, vars.size());
1516 const int candidate = vars[pos_var];
1521 if (!relaxed_variables_set[candidate]) {
1522 head_var = candidate;
1525 if (constraints_to_vars[ct].empty()) {
1532 if (vars_to_constraints[tail_var].empty()) {
1533 std::swap(active_vars[tail_index], active_vars.back());
1534 active_vars.pop_back();
1537 if (head_var != tail_var) {
1538 relaxed_variables_set[head_var] =
true;
1539 relaxed_variables.push_back(head_var);
1540 active_vars.push_back(head_var);
1543 return helper_.RelaxGivenVariables(initial_solution, relaxed_variables);
1549 const CpSolverResponse& initial_solution,
SolveData& data,
1550 absl::BitGenRef random) {
1551 const int num_model_constraints =
helper_.ModelProto().constraints_size();
1552 if (num_model_constraints == 0) {
1553 return helper_.FullNeighborhood();
1556 const int num_model_vars =
helper_.ModelProto().variables_size();
1557 std::vector<bool> visited_variables_set(num_model_vars,
false);
1558 std::vector<int> relaxed_variables;
1560 std::vector<bool> added_constraints(num_model_constraints,
false);
1561 std::vector<int> next_constraints;
1563 std::vector<int> random_variables;
1565 absl::ReaderMutexLock graph_lock(&
helper_.graph_mutex_);
1566 const int num_active_vars =
1567 helper_.ActiveVariablesWhileHoldingLock().size();
1568 const int target_size = std::ceil(data.
difficulty * num_active_vars);
1569 if (target_size == num_active_vars)
return helper_.FullNeighborhood();
1572 const int num_active_constraints =
helper_.ConstraintToVar().size();
1573 if (num_active_constraints != 0) {
1574 next_constraints.push_back(
1575 absl::Uniform<int>(random, 0, num_active_constraints));
1576 added_constraints[next_constraints.back()] =
true;
1579 while (relaxed_variables.size() < target_size) {
1581 if (next_constraints.empty())
break;
1584 const int i = absl::Uniform<int>(random, 0, next_constraints.size());
1585 const int constraint_index = next_constraints[
i];
1586 std::swap(next_constraints[
i], next_constraints.back());
1587 next_constraints.pop_back();
1591 DCHECK_LT(constraint_index, num_active_constraints);
1592 random_variables.assign(
1593 helper_.ConstraintToVar()[constraint_index].begin(),
1594 helper_.ConstraintToVar()[constraint_index].end());
1595 std::shuffle(random_variables.begin(), random_variables.end(), random);
1596 for (
const int var : random_variables) {
1597 if (visited_variables_set[var])
continue;
1598 visited_variables_set[var] =
true;
1600 relaxed_variables.push_back(var);
1602 if (relaxed_variables.size() >= target_size)
break;
1604 for (
const int ct :
helper_.VarToConstraint()[var]) {
1605 if (added_constraints[ct])
continue;
1606 added_constraints[ct] =
true;
1607 next_constraints.push_back(ct);
1613 return helper_.RelaxGivenVariables(initial_solution, relaxed_variables);
1617 const CpSolverResponse& initial_solution,
SolveData& data,
1618 absl::BitGenRef random) {
1620 int size_at_min_width_after_100;
1621 int min_width_after_100 = std::numeric_limits<int>::max();
1622 int num_zero_score = 0;
1623 std::vector<int> relaxed_variables;
1629 absl::ReaderMutexLock graph_lock(&
helper_.graph_mutex_);
1631 const int num_active_vars =
1632 helper_.ActiveVariablesWhileHoldingLock().size();
1633 const int target_size = std::ceil(data.
difficulty * num_active_vars);
1634 if (target_size == num_active_vars)
return helper_.FullNeighborhood();
1636 const int num_vars =
helper_.VarToConstraint().size();
1637 const int num_constraints =
helper_.ConstraintToVar().size();
1638 if (num_constraints == 0 || num_vars == 0) {
1639 return helper_.FullNeighborhood();
1644 const int num_nodes = num_vars + num_constraints;
1645 std::vector<bool> added(num_nodes,
false);
1646 std::vector<bool> added_or_connected(num_nodes,
false);
1649 struct QueueElement {
1650 int Index()
const {
return index; }
1651 bool operator<(
const QueueElement& o)
const {
1652 if (score == o.score)
return tie_break < o.tie_break;
1653 return score < o.score;
1658 double tie_break = 0.0;
1660 std::vector<QueueElement> elements(num_nodes);
1664 for (
int i = 0;
i < num_nodes; ++
i) {
1665 elements[
i].index =
i;
1666 elements[
i].tie_break = absl::Uniform<double>(random, 0.0, 1.0);
1676 const int first_index =
1677 helper_.ActiveVariablesWhileHoldingLock()[absl::Uniform<int>(
1678 random, 0, num_active_vars)];
1679 elements[first_index].score =
helper_.VarToConstraint()[first_index].size();
1680 pq.
Add(elements[first_index]);
1681 added_or_connected[first_index] =
true;
1684 std::vector<int> to_update;
1685 while (!pq.
IsEmpty() && relaxed_variables.size() < target_size) {
1687 if (relaxed_variables.size() > 100 && pq.
Size() < min_width_after_100) {
1688 min_width_after_100 = pq.
Size();
1689 size_at_min_width_after_100 = relaxed_variables.size();
1692 const int index = pq.
Top().index;
1693 const int score = pq.
Top().score;
1695 added[index] =
true;
1700 if (index < num_vars) relaxed_variables.push_back(index);
1710 if (index < num_vars) {
1711 relaxed_variables.push_back(index);
1712 for (
const int c :
helper_.VarToConstraint()[index]) {
1713 const int c_index = num_vars + c;
1714 if (added_or_connected[c_index])
continue;
1716 added_or_connected[c_index] =
true;
1717 to_update.push_back(c_index);
1718 for (
const int v :
helper_.ConstraintToVar()[c]) {
1719 if (added[v])
continue;
1720 if (added_or_connected[v]) {
1721 to_update.push_back(v);
1722 elements[v].score--;
1724 elements[c_index].score++;
1729 for (
const int v :
helper_.ConstraintToVar()[index - num_vars]) {
1730 if (added_or_connected[v])
continue;
1732 added_or_connected[v] =
true;
1733 to_update.push_back(v);
1734 for (
const int c :
helper_.VarToConstraint()[v]) {
1735 if (added[num_vars + c])
continue;
1736 if (added_or_connected[num_vars + c]) {
1737 elements[num_vars + c].score--;
1738 to_update.push_back(num_vars + c);
1740 elements[v].score++;
1749 CHECK_EQ(num_added, score);
1752 for (
const int index : to_update) {
1753 DCHECK(!added[index]);
1757 pq.
Add(elements[index]);
1761 max_width = std::max(max_width, pq.
Size());
1765 if (pq.
Size() < min_width_after_100) {
1766 min_width_after_100 = pq.
Size();
1767 size_at_min_width_after_100 = relaxed_variables.size();
1770 VLOG(2) <<
"#relaxed " << relaxed_variables.size() <<
" #zero_score "
1771 << num_zero_score <<
" max_width " << max_width
1772 <<
" (size,min_width)_after_100 (" << size_at_min_width_after_100
1773 <<
"," << min_width_after_100 <<
") "
1774 <<
" final_width " << pq.
Size();
1777 return helper_.RelaxGivenVariables(initial_solution, relaxed_variables);
1783ConstraintProto DistanceToBoundsSmallerThanConstraint(
1784 absl::Span<
const std::pair<int, int64_t>> dist_to_lower_bound,
1785 absl::Span<
const std::pair<int, int64_t>> dist_to_upper_bound,
1786 const int64_t rhs) {
1788 ConstraintProto new_constraint;
1789 LinearConstraintProto* linear = new_constraint.mutable_linear();
1790 int64_t lhs_constant_value = 0;
1791 for (
const auto [var, lb] : dist_to_lower_bound) {
1793 linear->add_coeffs(1);
1794 linear->add_vars(var);
1795 lhs_constant_value -= lb;
1797 for (
const auto [var, ub] : dist_to_upper_bound) {
1799 lhs_constant_value += ub;
1800 linear->add_coeffs(-1);
1801 linear->add_vars(var);
1803 linear->add_domain(std::numeric_limits<int64_t>::min());
1804 linear->add_domain(rhs - lhs_constant_value);
1805 return new_constraint;
1811 const CpSolverResponse& initial_solution,
SolveData& data,
1812 absl::BitGenRef random) {
1813 const std::vector<int> active_variables =
helper_.ActiveVariables();
1814 if (active_variables.empty())
return helper_.NoNeighborhood();
1820 const int size = active_variables.size();
1821 if (
static_cast<int>(std::ceil(data.
difficulty * size)) == size) {
1822 return helper_.FullNeighborhood();
1828 std::vector<std::pair<int, double>> candidates_with_score;
1829 std::vector<int> other_variables;
1833 std::vector<std::pair<int, int64_t>> dist_to_lower_bound;
1834 std::vector<std::pair<int, int64_t>> dist_to_upper_bound;
1838 const bool only_look_at_binary = absl::Bernoulli(random, 0.5);
1842 CpModelProto local_cp_model =
helper_.UpdatedModelProtoCopy();
1845 bool some_non_binary_at_bound =
false;
1846 for (
const int var : active_variables) {
1847 DCHECK_LT(var, initial_solution.solution().size());
1848 DCHECK_LT(var, local_cp_model.variables().size());
1849 const IntegerVariableProto& var_proto = local_cp_model.variables(var);
1850 const int64_t base_value = initial_solution.solution(var);
1851 const bool is_binary = var_proto.domain_size() == 2 &&
1852 var_proto.domain(0) == 0 && var_proto.domain(1) == 1;
1853 if (only_look_at_binary && !is_binary) {
1854 other_variables.push_back(var);
1858 DCHECK(!var_proto.domain().empty());
1859 const int64_t domain_min = var_proto.domain(0);
1860 const int64_t domain_max = var_proto.domain(var_proto.domain().size() - 1);
1861 if (base_value <= domain_min) {
1862 if (!is_binary) some_non_binary_at_bound =
true;
1863 candidates_with_score.push_back({var, 0.0});
1864 dist_to_lower_bound.push_back({var, domain_min});
1865 }
else if (base_value >= domain_max) {
1866 if (!is_binary) some_non_binary_at_bound =
true;
1867 candidates_with_score.push_back({var, 0.0});
1868 dist_to_upper_bound.push_back({var, domain_max});
1870 other_variables.push_back(var);
1874 bool use_hamming_for_others =
false;
1875 if (!other_variables.empty() && absl::Bernoulli(random, 0.5)) {
1876 use_hamming_for_others =
true;
1878 if (!use_hamming_for_others && candidates_with_score.empty()) {
1879 return helper_.NoNeighborhood();
1884 if (use_hamming_for_others) {
1885 for (
const int var : other_variables) {
1886 const int indicator = local_cp_model.variables().size();
1887 auto* var_proto = local_cp_model.add_variables();
1888 var_proto->add_domain(0);
1889 var_proto->add_domain(1);
1890 auto* new_ct = local_cp_model.add_constraints();
1891 new_ct->add_enforcement_literal(
NegatedRef(indicator));
1893 const int64_t base_value = initial_solution.solution(var);
1894 new_ct->mutable_linear()->add_domain(base_value);
1895 new_ct->mutable_linear()->add_domain(base_value);
1896 new_ct->mutable_linear()->add_vars(var);
1897 new_ct->mutable_linear()->add_coeffs(1);
1900 dist_to_lower_bound.push_back({indicator, 0});
1901 candidates_with_score.push_back({var, 0.0});
1905 other_variables.clear();
1909 const int size = dist_to_upper_bound.size() + dist_to_lower_bound.size();
1910 const int target_size =
static_cast<int>(std::ceil(data.
difficulty * size));
1911 DCHECK_LE(target_size, candidates_with_score.size());
1912 *local_cp_model.add_constraints() = DistanceToBoundsSmallerThanConstraint(
1913 dist_to_lower_bound, dist_to_upper_bound, target_size);
1915 Model model(
"lb_relax_lns_lp");
1916 auto*
const params = model.
GetOrCreate<SatParameters>();
1919 params->set_num_workers(1);
1920 params->set_linearization_level(2);
1921 params->set_stop_after_root_propagation(
true);
1922 params->set_add_lp_constraints_lazily(
false);
1925 params->set_cp_model_presolve(
false);
1926 params->set_cp_model_probing_level(0);
1930 params->set_root_lp_iterations(100000);
1934 params->set_max_deterministic_time(10);
1936 if (global_time_limit_ !=
nullptr) {
1942 if (local_cp_model.has_objective()) {
1943 local_cp_model.mutable_objective()->set_integer_before_offset(0);
1944 local_cp_model.mutable_objective()->set_integer_after_offset(0);
1945 local_cp_model.mutable_objective()->set_integer_scaling_factor(0);
1949 if (absl::GetFlag(FLAGS_cp_model_dump_submodels)) {
1950 const std::string dump_name =
1951 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
1952 "lb_relax_lns_lp_", data.
task_id,
".pb.txt");
1953 LOG(INFO) <<
"Dumping linear relaxed model to '" << dump_name <<
"'.";
1966 response_manager->InitializeObjective(local_cp_model);
1979 if (local_cp_model.has_objective()) {
1980 const CpSolverResponse response = response_manager->GetResponse();
1981 if (response.status() == CpSolverStatus::INFEASIBLE) {
1982 data.
status = CpSolverStatus::INFEASIBLE;
1984 return helper_.NoNeighborhood();
1987 const int64_t inner_lb = response.inner_objective_lower_bound();
1989 local_cp_model.objective(), initial_solution.solution());
1990 if (inner_lb >= current_inner_obj) {
1994 data.
status = CpSolverStatus::OPTIMAL;
1996 return helper_.NoNeighborhood();
2004 if (lp_solution->empty()) {
2006 return helper_.NoNeighborhood();
2008 for (
auto& [var, score] : candidates_with_score) {
2009 const IntegerVariable integer = var_mapping->Integer(var);
2010 DCHECK_LT(integer, lp_solution->size());
2011 DCHECK_LT(var, initial_solution.solution().size());
2012 const double difference =
2013 std::abs(lp_solution->at(var_mapping->Integer(var)) -
2014 initial_solution.solution(var));
2015 score = difference + absl::Uniform<double>(random, 0.0, 1e-6);
2019 absl::c_sort(candidates_with_score, [](
const std::pair<int, double>& a,
2020 const std::pair<int, double>&
b) {
2021 return a.second >
b.second;
2024 std::vector<int> vars_to_relax;
2025 vars_to_relax.
reserve(target_size);
2026 DCHECK_LE(target_size, candidates_with_score.size());
2027 for (
int i = 0;
i < target_size; ++
i) {
2028 vars_to_relax.push_back(candidates_with_score[
i].first);
2033 vars_to_relax.insert(vars_to_relax.end(), other_variables.begin(),
2034 other_variables.end());
2036 helper_.RelaxGivenVariables(initial_solution, vars_to_relax);
2044 some_non_binary_at_bound ?
"_int" :
"_bool");
2045 if (use_hamming_for_others) {
2054void AddPrecedence(
const LinearExpressionProto& before,
2055 const LinearExpressionProto& after, CpModelProto* model) {
2056 LinearConstraintProto* linear = model->add_constraints()->mutable_linear();
2057 linear->add_domain(std::numeric_limits<int64_t>::min());
2058 linear->add_domain(after.offset() - before.offset());
2059 for (
int i = 0;
i < before.vars_size(); ++
i) {
2060 linear->add_vars(before.vars(
i));
2061 linear->add_coeffs(before.coeffs(
i));
2063 for (
int i = 0;
i < after.vars_size(); ++
i) {
2064 linear->add_vars(after.vars(
i));
2065 linear->add_coeffs(-after.coeffs(
i));
2072 const absl::Span<
const std::pair<int, int>> precedences,
2073 const CpSolverResponse& initial_solution,
2077 neighborhood.
is_reduced = !precedences.empty();
2081 return neighborhood;
2085 absl::flat_hash_set<int> seen_intervals;
2086 for (
const std::pair<int, int>& prec : precedences) {
2087 seen_intervals.insert(prec.first);
2088 seen_intervals.insert(prec.second);
2092 bool enforcement_literals_fixed =
false;
2094 if (seen_intervals.contains(
i))
continue;
2096 const ConstraintProto& interval_ct = helper.
ModelProto().constraints(
i);
2097 if (interval_ct.enforcement_literal().empty())
continue;
2099 DCHECK_EQ(interval_ct.enforcement_literal().size(), 1);
2100 const int enforcement_ref = interval_ct.enforcement_literal(0);
2101 const int enforcement_var =
PositiveRef(enforcement_ref);
2102 const int value = initial_solution.solution(enforcement_var);
2109 if (
RefIsPositive(enforcement_ref) == (value == 0))
continue;
2112 neighborhood.
delta.mutable_variables(enforcement_var)->clear_domain();
2113 neighborhood.
delta.mutable_variables(enforcement_var)->add_domain(value);
2114 neighborhood.
delta.mutable_variables(enforcement_var)->add_domain(value);
2115 enforcement_literals_fixed =
true;
2118 for (
const std::pair<int, int>& prec : precedences) {
2119 const LinearExpressionProto& before_end =
2120 helper.
ModelProto().constraints(prec.first).interval().end();
2121 const LinearExpressionProto& after_start =
2122 helper.
ModelProto().constraints(prec.second).interval().start();
2123 DCHECK_LE(GetLinearExpressionValue(before_end, initial_solution),
2124 GetLinearExpressionValue(after_start, initial_solution));
2125 AddPrecedence(before_end, after_start, &neighborhood.
delta);
2132 return neighborhood;
2136 absl::Span<const int> intervals_to_relax,
2137 absl::Span<const int> variables_to_fix,
2138 const CpSolverResponse& initial_solution, absl::BitGenRef random,
2143 absl::flat_hash_set<int> ignored_intervals(intervals_to_relax.begin(),
2144 intervals_to_relax.end());
2149 if (ignored_intervals.contains(
i))
continue;
2151 const ConstraintProto& interval_ct = helper.
ModelProto().constraints(
i);
2152 if (interval_ct.enforcement_literal().empty())
continue;
2154 DCHECK_EQ(interval_ct.enforcement_literal().size(), 1);
2155 const int enforcement_ref = interval_ct.enforcement_literal(0);
2156 const int enforcement_var =
PositiveRef(enforcement_ref);
2157 const int value = initial_solution.solution(enforcement_var);
2165 ignored_intervals.insert(
i);
2170 neighborhood.
delta.mutable_variables(enforcement_var)->clear_domain();
2171 neighborhood.
delta.mutable_variables(enforcement_var)->add_domain(value);
2172 neighborhood.
delta.mutable_variables(enforcement_var)->add_domain(value);
2175 if (ignored_intervals.size() >=
2180 return neighborhood;
2189 const std::vector<std::pair<int, int>> precedences =
2192 for (
const std::pair<int, int>& prec : precedences) {
2193 const LinearExpressionProto& before_end =
2194 helper.
ModelProto().constraints(prec.first).interval().end();
2195 const LinearExpressionProto& after_start =
2196 helper.
ModelProto().constraints(prec.second).interval().start();
2197 DCHECK_LE(GetLinearExpressionValue(before_end, initial_solution),
2198 GetLinearExpressionValue(after_start, initial_solution));
2199 AddPrecedence(before_end, after_start, &neighborhood.
delta);
2203 for (
const int var : variables_to_fix) {
2204 const int value = initial_solution.solution(var);
2205 neighborhood.
delta.mutable_variables(var)->clear_domain();
2206 neighborhood.
delta.mutable_variables(var)->add_domain(value);
2207 neighborhood.
delta.mutable_variables(var)->add_domain(value);
2214 return neighborhood;
2218 const CpSolverResponse& initial_solution,
SolveData& data,
2219 absl::BitGenRef random) {
2220 std::vector<int> intervals_to_relax =
2221 helper_.GetActiveIntervals(initial_solution);
2222 GetRandomSubset(data.
difficulty, &intervals_to_relax, random);
2225 intervals_to_relax, {}, initial_solution, random,
helper_);
2229 const CpSolverResponse& initial_solution,
SolveData& data,
2230 absl::BitGenRef random) {
2231 std::vector<std::pair<int, int>> precedences =
2232 helper_.GetSchedulingPrecedences({}, initial_solution, random);
2233 GetRandomSubset(1.0 - data.
difficulty, &precedences, random);
2235 precedences, initial_solution,
helper_);
2239void AppendVarsFromAllIntervalIndices(absl::Span<const int> indices,
2240 absl::Span<const int> all_intervals,
2241 const CpModelProto& model_proto,
2242 std::vector<int>* variables) {
2243 for (
const int index : indices) {
2244 const std::vector<int> vars =
2245 UsedVariables(model_proto.constraints(all_intervals[index]));
2246 variables->insert(variables->end(), vars.begin(), vars.end());
2252 const CpSolverResponse& initial_solution,
SolveData& data,
2253 absl::BitGenRef random) {
2254 const std::vector<int> active_intervals =
2255 helper_.GetActiveIntervals(initial_solution);
2257 if (active_intervals.empty())
return helper_.FullNeighborhood();
2259 const TimePartition partition = PartitionIndicesAroundRandomTimeWindow(
2262 std::vector<int> intervals_to_relax;
2263 intervals_to_relax.reserve(partition.selected_indices.size());
2264 std::vector<int> variables_to_fix;
2265 intervals_to_relax.insert(intervals_to_relax.end(),
2266 partition.selected_indices.begin(),
2267 partition.selected_indices.end());
2269 if (
helper_.Parameters().push_all_tasks_toward_start()) {
2270 intervals_to_relax.insert(intervals_to_relax.end(),
2271 partition.indices_before_selected.begin(),
2272 partition.indices_before_selected.end());
2273 AppendVarsFromAllIntervalIndices(partition.indices_before_selected,
2274 active_intervals,
helper_.ModelProto(),
2281 intervals_to_relax, variables_to_fix, initial_solution, random,
helper_);
2285 const CpSolverResponse& initial_solution,
SolveData& data,
2286 absl::BitGenRef random) {
2287 std::vector<int> intervals_to_relax;
2288 std::vector<int> variables_to_fix;
2289 std::vector<int> active_intervals;
2290 for (
const std::vector<int>& intervals : intervals_in_constraints_) {
2291 active_intervals =
helper_.KeepActiveIntervals(intervals, initial_solution);
2292 const TimePartition partition = PartitionIndicesAroundRandomTimeWindow(
2293 active_intervals,
helper_.ModelProto(), initial_solution,
2295 intervals_to_relax.insert(intervals_to_relax.end(),
2296 partition.selected_indices.begin(),
2297 partition.selected_indices.end());
2299 if (
helper_.Parameters().push_all_tasks_toward_start()) {
2300 intervals_to_relax.insert(intervals_to_relax.end(),
2301 partition.indices_before_selected.begin(),
2302 partition.indices_before_selected.end());
2303 AppendVarsFromAllIntervalIndices(partition.indices_before_selected,
2304 active_intervals,
helper_.ModelProto(),
2309 if (intervals_to_relax.empty() && variables_to_fix.empty()) {
2310 return helper_.FullNeighborhood();
2316 intervals_to_relax, variables_to_fix, initial_solution, random,
helper_);
2320 const CpSolverResponse& initial_solution,
SolveData& data,
2321 absl::BitGenRef random) {
2322 std::vector<ActiveRectangle> rectangles_to_freeze =
2323 helper_.GetActiveRectangles(initial_solution);
2324 GetRandomSubset(1.0 - data.
difficulty, &rectangles_to_freeze, random);
2329 variables_to_freeze);
2331 variables_to_freeze);
2333 return helper_.FixGivenVariables(initial_solution, variables_to_freeze);
2337 const CpSolverResponse& initial_solution,
SolveData& data,
2338 absl::BitGenRef random) {
2340 const std::vector<ActiveRectangle> all_active_rectangles =
2341 helper_.GetActiveRectangles(initial_solution);
2342 if (all_active_rectangles.size() <= 1)
return helper_.FullNeighborhood();
2345 all_active_rectangles[absl::Uniform<int>(random, 0,
2346 all_active_rectangles.size())];
2348 const auto get_rectangle = [&initial_solution, helper = &
helper_](
2350 const int x_interval_idx = rectangle.x_interval;
2351 const int y_interval_idx = rectangle.y_interval;
2352 const ConstraintProto& x_interval_ct =
2353 helper->ModelProto().constraints(x_interval_idx);
2354 const ConstraintProto& y_interval_ct =
2355 helper->ModelProto().constraints(y_interval_idx);
2356 return Rectangle{.x_min = GetLinearExpressionValue(
2357 x_interval_ct.interval().start(), initial_solution),
2358 .x_max = GetLinearExpressionValue(
2359 x_interval_ct.interval().end(), initial_solution),
2360 .y_min = GetLinearExpressionValue(
2361 y_interval_ct.interval().start(), initial_solution),
2362 .y_max = GetLinearExpressionValue(
2363 y_interval_ct.interval().end(), initial_solution)};
2366 const Rectangle center_rect = get_rectangle(base_rectangle);
2375 std::vector<std::pair<int, double>> distances;
2376 distances.reserve(all_active_rectangles.size());
2377 for (
int i = 0;
i < all_active_rectangles.size(); ++
i) {
2380 variables_to_freeze);
2382 variables_to_freeze);
2384 const Rectangle rect = get_rectangle(rectangle);
2385 const bool same_no_overlap_as_center_rect = absl::c_any_of(
2387 return rectangle.no_overlap_2d_constraints.contains(c);
2389 if (same_no_overlap_as_center_rect) {
2390 distances.push_back(
2395 distances.begin(), distances.end(),
2396 [](
const auto& a,
const auto&
b) { return a.second < b.second; });
2398 const int num_to_sample = data.
difficulty * all_active_rectangles.size();
2399 const int num_to_relax = std::min<int>(distances.size(), num_to_sample);
2400 Rectangle relaxed_bounding_box = center_rect;
2401 absl::flat_hash_set<int> boxes_to_relax;
2402 for (
int i = 0;
i < num_to_relax; ++
i) {
2403 const int rectangle_idx = distances[
i].first;
2404 const ActiveRectangle& rectangle = all_active_rectangles[rectangle_idx];
2405 relaxed_bounding_box.
GrowToInclude(get_rectangle(rectangle));
2406 boxes_to_relax.insert(rectangle_idx);
2412 const IntegerValue x_size = relaxed_bounding_box.
SizeX();
2413 const IntegerValue y_size = relaxed_bounding_box.
SizeY();
2419 for (
const int b : boxes_to_relax) {
2422 variables_to_freeze);
2424 variables_to_freeze);
2427 helper_.FixGivenVariables(initial_solution, variables_to_freeze);
2434 for (
const int b : boxes_to_relax) {
2436 const IntervalConstraintProto& x_interval =
2438 .constraints(all_active_rectangles[
b].x_interval)
2441 relaxed_bounding_box.
x_max.value());
2442 RestrictAffineExpression(x_interval.start(), x_domain,
2443 &neighborhood.
delta);
2444 RestrictAffineExpression(x_interval.end(), x_domain, &neighborhood.
delta);
2447 const IntervalConstraintProto& y_interval =
2449 .constraints(all_active_rectangles[
b].y_interval)
2452 relaxed_bounding_box.
y_max.value());
2453 RestrictAffineExpression(y_interval.start(), y_domain,
2454 &neighborhood.
delta);
2455 RestrictAffineExpression(y_interval.end(), y_domain, &neighborhood.
delta);
2458 return neighborhood;
2462 const CpSolverResponse& initial_solution,
SolveData& data,
2463 absl::BitGenRef random) {
2465 std::vector<ActiveRectangle> all_active_rectangles =
2466 helper_.GetActiveRectangles(initial_solution);
2467 if (all_active_rectangles.size() <= 2)
return helper_.FullNeighborhood();
2469 const int first_idx =
2470 absl::Uniform<int>(random, 0, all_active_rectangles.size());
2472 absl::Uniform<int>(random, 0, all_active_rectangles.size() - 1);
2473 if (second_idx >= first_idx) {
2477 const ActiveRectangle& chosen_rectangle_1 = all_active_rectangles[first_idx];
2478 const ActiveRectangle& chosen_rectangle_2 = all_active_rectangles[second_idx];
2480 const auto get_rectangle = [&initial_solution, helper = &
helper_](
2482 const int x_interval_idx = rectangle.x_interval;
2483 const int y_interval_idx = rectangle.y_interval;
2484 const ConstraintProto& x_interval_ct =
2485 helper->ModelProto().constraints(x_interval_idx);
2486 const ConstraintProto& y_interval_ct =
2487 helper->ModelProto().constraints(y_interval_idx);
2488 return Rectangle{.x_min = GetLinearExpressionValue(
2489 x_interval_ct.interval().start(), initial_solution),
2490 .x_max = GetLinearExpressionValue(
2491 x_interval_ct.interval().end(), initial_solution),
2492 .y_min = GetLinearExpressionValue(
2493 y_interval_ct.interval().start(), initial_solution),
2494 .y_max = GetLinearExpressionValue(
2495 y_interval_ct.interval().end(), initial_solution)};
2498 const Rectangle rect1 = get_rectangle(chosen_rectangle_1);
2499 const Rectangle rect2 = get_rectangle(chosen_rectangle_2);
2509 std::vector<std::pair<int, double>> distances1;
2510 std::vector<std::pair<int, double>> distances2;
2511 distances1.reserve(all_active_rectangles.size());
2512 distances2.reserve(all_active_rectangles.size());
2513 for (
int i = 0;
i < all_active_rectangles.size(); ++
i) {
2516 variables_to_freeze);
2518 variables_to_freeze);
2520 const Rectangle rect = get_rectangle(rectangle);
2521 const bool same_no_overlap_as_rect1 =
2523 [&rectangle](
const int c) {
2524 return rectangle.no_overlap_2d_constraints.contains(c);
2526 const bool same_no_overlap_as_rect2 =
2528 [&rectangle](
const int c) {
2529 return rectangle.no_overlap_2d_constraints.contains(c);
2531 if (same_no_overlap_as_rect1) {
2534 if (same_no_overlap_as_rect2) {
2538 const int num_to_sample_each =
2539 data.
difficulty * all_active_rectangles.size() / 2;
2540 std::sort(distances1.begin(), distances1.end(),
2541 [](
const auto& a,
const auto&
b) { return a.second < b.second; });
2542 std::sort(distances2.begin(), distances2.end(),
2543 [](
const auto& a,
const auto&
b) { return a.second < b.second; });
2544 for (
auto& samples : {distances1, distances2}) {
2545 const int num_potential_samples = samples.size();
2546 for (
int i = 0;
i < std::min(num_potential_samples, num_to_sample_each);
2548 const int rectangle_idx = samples[
i].first;
2549 const ActiveRectangle& rectangle = all_active_rectangles[rectangle_idx];
2551 variables_to_freeze);
2553 variables_to_freeze);
2557 return helper_.FixGivenVariables(initial_solution, variables_to_freeze);
2561 const CpSolverResponse& initial_solution,
SolveData& data,
2562 absl::BitGenRef random) {
2563 std::vector<ActiveRectangle> rectangles_to_relax =
2564 helper_.GetActiveRectangles(initial_solution);
2565 GetRandomSubset(data.
difficulty, &rectangles_to_relax, random);
2566 std::vector<int> intervals_to_relax;
2568 intervals_to_relax.push_back(rect.x_interval);
2569 intervals_to_relax.push_back(rect.y_interval);
2574 intervals_to_relax, {}, initial_solution, random,
helper_);
2578 const CpSolverResponse& initial_solution,
SolveData& data,
2579 absl::BitGenRef random) {
2580 const std::vector<ActiveRectangle> active_rectangles =
2581 helper_.GetActiveRectangles(initial_solution);
2582 const bool use_first_dimension = absl::Bernoulli(random, 0.5);
2583 std::vector<int> projected_intervals;
2584 projected_intervals.reserve(active_rectangles.size());
2586 projected_intervals.push_back(use_first_dimension ? rect.x_interval
2590 const TimePartition partition = PartitionIndicesAroundRandomTimeWindow(
2591 projected_intervals,
helper_.ModelProto(), initial_solution,
2593 std::vector<bool> indices_to_fix(active_rectangles.size(),
true);
2594 for (
const int index : partition.selected_indices) {
2595 indices_to_fix[index] =
false;
2599 for (
int index = 0; index < active_rectangles.size(); ++index) {
2600 if (indices_to_fix[index]) {
2602 active_rectangles[index].x_interval,
2603 variables_to_freeze);
2605 active_rectangles[index].y_interval,
2606 variables_to_freeze);
2610 return helper_.FixGivenVariables(initial_solution, variables_to_freeze);
2614 const CpSolverResponse& initial_solution,
SolveData& data,
2615 absl::BitGenRef random) {
2616 const std::vector<std::vector<int>> all_paths =
2617 helper_.GetRoutingPathBooleanVariables(initial_solution);
2620 std::vector<int> variables_to_fix;
2621 for (
const auto& path : all_paths) {
2622 variables_to_fix.insert(variables_to_fix.end(), path.begin(), path.end());
2625 GetRandomSubset(1.0 - data.
difficulty, &variables_to_fix, random);
2628 for (
const int var : variables_to_fix) to_fix.
Set(var);
2629 return helper_.FixGivenVariables(initial_solution, to_fix);
2633 const CpSolverResponse& initial_solution,
SolveData& data,
2634 absl::BitGenRef random) {
2635 std::vector<std::vector<int>> all_paths =
2636 helper_.GetRoutingPathBooleanVariables(initial_solution);
2639 if (all_paths.empty()) {
2640 return helper_.NoNeighborhood();
2644 std::vector<int> all_path_variables;
2645 int sum_of_path_sizes = 0;
2646 for (
const auto& path : all_paths) {
2647 sum_of_path_sizes += path.size();
2649 all_path_variables.reserve(sum_of_path_sizes);
2650 for (
const auto& path : all_paths) {
2651 all_path_variables.insert(all_path_variables.end(), path.begin(),
2657 const int num_variables_to_relax =
2658 static_cast<int>(all_path_variables.size() * data.
difficulty);
2659 absl::flat_hash_set<int> relaxed_variables;
2661 while (relaxed_variables.size() < num_variables_to_relax) {
2662 DCHECK(!all_paths.empty());
2663 const int path_index = absl::Uniform<int>(random, 0, all_paths.size());
2664 std::vector<int>& path = all_paths[path_index];
2665 const int path_size = path.size();
2666 const int segment_length =
2667 std::min(path_size, absl::Uniform<int>(random, 4, 8));
2668 const int segment_start =
2669 absl::Uniform<int>(random, 0, path_size - segment_length);
2670 for (
int i = segment_start;
i < segment_start + segment_length; ++
i) {
2671 relaxed_variables.insert(path[
i]);
2675 path.erase(path.begin() + segment_start,
2676 path.begin() + segment_start + segment_length);
2678 std::swap(all_paths[path_index], all_paths.back());
2679 all_paths.pop_back();
2685 for (
const int var : all_path_variables) {
2686 if (!relaxed_variables.contains(var)) to_fix.
Set(var);
2688 return helper_.FixGivenVariables(initial_solution, to_fix);
2692 const CpSolverResponse& initial_solution,
SolveData& data,
2693 absl::BitGenRef random) {
2694 std::vector<std::vector<int>> all_paths =
2695 helper_.GetRoutingPathBooleanVariables(initial_solution);
2698 if (all_paths.empty()) {
2699 return helper_.NoNeighborhood();
2703 std::vector<int> all_path_variables;
2704 int sum_of_path_sizes = 0;
2705 for (
const auto& path : all_paths) {
2706 sum_of_path_sizes += path.size();
2708 all_path_variables.reserve(sum_of_path_sizes);
2709 for (
const auto& path : all_paths) {
2710 all_path_variables.insert(all_path_variables.end(), path.begin(),
2716 const int num_variables_to_relax =
2717 static_cast<int>(all_path_variables.size() * data.
difficulty);
2718 absl::flat_hash_set<int> relaxed_variables;
2722 for (
const auto& path : all_paths) {
2723 relaxed_variables.insert(path.front());
2724 relaxed_variables.insert(path.back());
2728 const int path_index = absl::Uniform<int>(random, 0, all_paths.size());
2729 std::shuffle(all_paths[path_index].begin(), all_paths[path_index].end(),
2731 while (relaxed_variables.size() < num_variables_to_relax &&
2732 !all_paths[path_index].empty()) {
2733 relaxed_variables.insert(all_paths[path_index].back());
2734 all_paths[path_index].pop_back();
2738 if (relaxed_variables.size() < num_variables_to_relax) {
2739 std::shuffle(all_path_variables.begin(), all_path_variables.end(), random);
2740 while (relaxed_variables.size() < num_variables_to_relax) {
2741 relaxed_variables.insert(all_path_variables.back());
2742 all_path_variables.pop_back();
2748 for (
const int var : all_path_variables) {
2749 if (!relaxed_variables.contains(var)) to_fix.
Set(var);
2751 return helper_.FixGivenVariables(initial_solution, to_fix);
2755 return (incomplete_solutions_->HasSolution() ||
2756 lp_solutions_->NumSolutions() > 0);
2760 const CpSolverResponse& ,
SolveData& data,
2761 absl::BitGenRef random) {
2767 incomplete_solutions_, data.
difficulty, random);
2771 return neighborhood;
2775 absl::ReaderMutexLock graph_lock(&
helper_.graph_mutex_);
2777 for (
const std::pair</*model_var*/ int, /*value*/ int64_t>& fixed_var :
2779 const int var = fixed_var.first;
2780 const int64_t value = fixed_var.second;
2781 if (var >= neighborhood.
delta.variables_size())
continue;
2782 if (!
helper_.IsActive(var))
continue;
2786 return neighborhood;
2789 neighborhood.
delta.mutable_variables(var)->clear_domain();
2790 neighborhood.
delta.mutable_variables(var)->add_domain(value);
2791 neighborhood.
delta.mutable_variables(var)->add_domain(value);
2795 for (
const std::pair<
int,
2796 std::pair<int64_t, int64_t>>& reduced_var :
2798 const int var = reduced_var.first;
2799 const int64_t lb = reduced_var.second.first;
2800 const int64_t ub = reduced_var.second.second;
2801 if (var >= neighborhood.
delta.variables_size())
continue;
2802 if (!
helper_.IsActive(var))
continue;
2814 return neighborhood;
bool AddEdge(int node1, int node2)
void SetNumberOfNodes(int num_nodes)
IndexType size() const
Returns how many bits this Bitset64 can hold.
void Clear(IndexType i)
Sets the bit at position i to 0.
void Set(IndexType i)
Sets the bit at position i to 1.
static Domain FromValues(std::vector< int64_t > values)
Domain IntersectionWith(const Domain &domain) const
bool IsIncludedIn(const Domain &domain) const
int64_t ClosestValue(int64_t wanted) const
void Add(Element element)
void ChangePriority(Element element)
int Size() const
Returns the number of elements currently present.
bool Contains(int index) const
Returns true if the element with given index is currently in the queue.
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
void RemoveBySwap(K key, int index)
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood FullNeighborhood() const
Returns a neighborhood that correspond to the full problem.
bool IsActive(int var) const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_)
CpModelProto UpdatedModelProtoCopy() const
Returns a copy of the problem proto with updated domains.
std::vector< std::vector< int > > GetRoutingPathBooleanVariables(const CpSolverResponse &initial_solution) const
Neighborhood FixAllVariables(const CpSolverResponse &initial_solution) const
Neighborhood NoNeighborhood() const
Neighborhood FixGivenVariables(const CpSolverResponse &base_solution, const Bitset64< int > &variables_to_fix) const
const SharedResponseManager & shared_response() const
Neighborhood RelaxGivenVariables(const CpSolverResponse &initial_solution, absl::Span< const int > relaxed_variables) const
void Synchronize() override
NeighborhoodGeneratorHelper(CpModelProto const *model_proto, SatParameters const *parameters, SharedResponseManager *shared_response, SharedBoundsManager *shared_bounds=nullptr)
std::vector< std::vector< int > > GetUniqueIntervalSets() const
std::vector< ActiveRectangle > GetActiveRectangles(const CpSolverResponse &initial_solution) const
const CpModelProto & ModelProto() const
void AddSolutionHinting(const CpSolverResponse &initial_solution, CpModelProto *model_proto) const
std::vector< int > GetActiveIntervals(const CpSolverResponse &initial_solution) const
std::vector< std::pair< int, int > > GetSchedulingPrecedences(const absl::flat_hash_set< int > &ignored_intervals, const CpSolverResponse &initial_solution, absl::BitGenRef random) const
absl::Span< const int > TypeToConstraints(ConstraintProto::ConstraintCase type) const
Returns all the constraints indices of a given type.
std::vector< int > KeepActiveIntervals(absl::Span< const int > unfiltered_intervals, const CpSolverResponse &initial_solution) const
NeighborhoodGeneratorHelper::ActiveRectangle ActiveRectangle
virtual bool ReadyToGenerate() const
Returns true if the neighborhood generator can generate a neighborhood.
double deterministic_limit_
void AddSolveData(SolveData data)
absl::Mutex generator_mutex_
double GetUCBScore(int64_t total_num_calls) const
const NeighborhoodGeneratorHelper & helper_
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Both initial solution and difficulty values are ignored.
bool ReadyToGenerate() const override
Returns true if the required solutions are available.
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
SubSolver(absl::string_view name, SubsolverType type)
SubsolverType type() const
Returns the type of the subsolver.
Neighborhood Generate(const CpSolverResponse &initial_solution, SolveData &data, absl::BitGenRef random) final
void reserve(size_type n)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void SolveLoadedCpModel(const CpModelProto &model_proto, Model *model)
void RemoveVariablesFromInterval(const CpModelProto &model_proto, int index, Bitset64< int > &output)
bool RefIsPositive(int ref)
int64_t ComputeInnerObjective(const CpObjectiveProto &objective, absl::Span< const int64_t > solution)
ReducedDomainNeighborhood GetRinsRensNeighborhood(const SharedResponseManager *response_manager, const SharedLPSolutionRepository *lp_solutions, SharedIncompleteSolutionManager *incomplete_solutions, double difficulty, absl::BitGenRef random)
bool WriteModelProtoToFile(const M &proto, absl::string_view filename)
bool DomainInProtoContains(const ProtoWithDomain &proto, int64_t value)
Neighborhood GenerateSchedulingNeighborhoodFromIntervalPrecedences(const absl::Span< const std::pair< int, int > > precedences, const CpSolverResponse &initial_solution, const NeighborhoodGeneratorHelper &helper)
double CenterToCenterLInfinityDistance(const Rectangle &a, const Rectangle &b)
Neighborhood GenerateSchedulingNeighborhoodFromRelaxedIntervals(absl::Span< const int > intervals_to_relax, absl::Span< const int > variables_to_fix, const CpSolverResponse &initial_solution, absl::BitGenRef random, const NeighborhoodGeneratorHelper &helper)
double CenterToCenterL2Distance(const Rectangle &a, const Rectangle &b)
Returns the L2 distance between the centers of the two rectangles.
std::vector< int > UsedVariables(const ConstraintProto &ct)
std::vector< int > UsedIntervals(const ConstraintProto &ct)
Returns the sorted list of interval used by a constraint.
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
Serializes a Domain into the domain field of a proto.
IntegerValue CapAddI(IntegerValue a, IntegerValue b)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Reads a Domain from the domain field of a proto.
void LoadCpModel(const CpModelProto &model_proto, Model *model)
int NegatedRef(int ref)
Small utility functions to deal with negative variable/literal references.
void InsertVariablesFromInterval(const CpModelProto &model_proto, int index, Bitset64< int > &output)
Insert/Remove variables from an interval constraint into a bitset.
IntegerValue CapSubI(IntegerValue a, IntegerValue b)
In SWIG mode, we don't want anything besides these top-level includes.
int64_t CapSub(int64_t x, int64_t y)
absl::flat_hash_set< int > no_overlap_2d_constraints
Adds solve data about one "solved" neighborhood.
double deterministic_time
The time it took to solve this neighborhood.
double difficulty
The difficulty when this neighborhood was generated.
CpSolverStatus status
The status of the sub-solve.
int task_id
For debugging.
Neighborhood returned by Neighborhood generators.
int num_relaxed_variables_in_objective
std::string source_info
Overwrites the name of the neighborhood generator in the logs.
bool is_simple
True if this neighborhood was just obtained by fixing some variables.
int num_relaxed_variables
Statistic, only filled when is_simple is true.
bool is_generated
True if neighborhood generator was able to generate a neighborhood.
std::vector< int > variables_that_can_be_fixed_to_local_optimum
static constexpr int kDefaultArenaSizePerVariable
void GrowToInclude(const Rectangle &other)
IntegerValue SizeY() const
IntegerValue SizeX() const
std::vector< std::pair< int, std::pair< int64_t, int64_t > > > reduced_domain_vars
std::vector< std::pair< int, int64_t > > fixed_vars
A variable will appear only once and not in both vectors.