28#include "absl/algorithm/container.h"
29#include "absl/base/log_severity.h"
30#include "absl/container/flat_hash_map.h"
31#include "absl/container/flat_hash_set.h"
32#include "absl/log/check.h"
33#include "absl/meta/type_traits.h"
34#include "absl/random/bit_gen_ref.h"
35#include "absl/random/distributions.h"
36#include "absl/strings/str_cat.h"
37#include "absl/strings/str_join.h"
38#include "absl/synchronization/mutex.h"
39#include "absl/types/span.h"
40#include "google/protobuf/arena.h"
44#include "ortools/sat/cp_model.pb.h"
54#include "ortools/sat/sat_parameters.pb.h"
69 CpModelProto
const* model_proto, SatParameters
const*
parameters,
71 :
SubSolver(
"neighborhood_helper", HELPER),
73 model_proto_(*model_proto),
74 shared_bounds_(shared_bounds),
75 shared_response_(shared_response) {
77 simplified_model_proto_ =
78 google::protobuf::Arena::Create<CpModelProto>(&local_arena_);
79 model_proto_with_only_variables_ =
80 google::protobuf::Arena::Create<CpModelProto>(&local_arena_);
82 CHECK(shared_response_ !=
nullptr);
83 if (shared_bounds_ !=
nullptr) {
86 *model_proto_with_only_variables_->mutable_variables() =
87 model_proto_.variables();
88 InitializeHelperData();
89 RecomputeHelperData();
94 if (shared_bounds_ !=
nullptr) {
95 std::vector<int> model_variables;
96 std::vector<int64_t> new_lower_bounds;
97 std::vector<int64_t> new_upper_bounds;
99 &new_lower_bounds, &new_upper_bounds);
101 bool new_variables_have_been_fixed =
false;
104 absl::MutexLock domain_lock(&domain_mutex_);
106 for (
int i = 0;
i < model_variables.size(); ++
i) {
107 const int var = model_variables[
i];
108 const int64_t new_lb = new_lower_bounds[
i];
109 const int64_t new_ub = new_upper_bounds[
i];
112 model_proto_with_only_variables_->variables(
var).domain();
113 const int64_t old_lb = domain.Get(0);
114 const int64_t old_ub = domain.Get(domain.size() - 1);
115 VLOG(3) <<
"Variable: " <<
var <<
" old domain: [" << old_lb <<
", "
116 << old_ub <<
"] new domain: [" << new_lb <<
", " << new_ub
120 model_proto_with_only_variables_->variables(
var));
141 model_proto_with_only_variables_->mutable_variables(
var));
142 new_variables_have_been_fixed |= new_domain.
IsFixed();
147 if (new_variables_have_been_fixed) {
148 RecomputeHelperData();
153bool NeighborhoodGeneratorHelper::ObjectiveDomainIsConstraining()
const {
154 if (!model_proto_.has_objective())
return false;
155 if (model_proto_.objective().domain().empty())
return false;
157 int64_t min_activity = 0;
158 int64_t max_activity = 0;
159 const int num_terms = model_proto_.objective().vars().size();
160 for (
int i = 0;
i < num_terms; ++
i) {
162 const int64_t coeff = model_proto_.objective().coeffs(
i);
163 const auto& var_domain =
164 model_proto_with_only_variables_->variables(
var).domain();
165 const int64_t v1 = coeff * var_domain[0];
166 const int64_t v2 = coeff * var_domain[var_domain.size() - 1];
167 min_activity += std::min(v1, v2);
168 max_activity += std::max(v1, v2);
172 const Domain inferred_domain =
173 Domain(min_activity, max_activity)
175 Domain(std::numeric_limits<int64_t>::min(), obj_domain.
Max()));
179void NeighborhoodGeneratorHelper::InitializeHelperData() {
180 type_to_constraints_.clear();
181 const int num_constraints = model_proto_.constraints_size();
182 for (
int c = 0; c < num_constraints; ++c) {
183 const int type = model_proto_.constraints(c).constraint_case();
184 if (
type >= type_to_constraints_.size()) {
185 type_to_constraints_.resize(
type + 1);
187 type_to_constraints_[
type].push_back(c);
190 const int num_variables = model_proto_.variables().size();
191 is_in_objective_.resize(num_variables,
false);
192 if (model_proto_.has_objective()) {
193 for (
const int ref : model_proto_.objective().vars()) {
201void NeighborhoodGeneratorHelper::RecomputeHelperData() {
203 absl::ReaderMutexLock domain_lock(&domain_mutex_);
217 CpModelProto mapping_proto;
218 simplified_model_proto_->Clear();
219 *simplified_model_proto_->mutable_variables() =
220 model_proto_with_only_variables_->variables();
221 PresolveContext
context(&local_model, simplified_model_proto_,
227 copier.ImportAndSimplifyConstraints(model_proto_, {});
233 const auto& constraints = simplified_model_proto_->constraints();
234 constraint_to_var_.clear();
235 constraint_to_var_.reserve(constraints.size());
242 if (constraints[
ct_index].constraint_case() == ConstraintProto::kInterval) {
248 if (IsConstant(
var))
continue;
249 tmp_row_.push_back(
var);
254 bool need_sort =
false;
258 if (IsConstant(
var))
continue;
259 tmp_row_.push_back(
var);
265 if (tmp_row_.size() <= 1) {
273 constraint_to_var_.Add(tmp_row_);
278 var_to_constraint_.ResetFromTranspose(
280 model_proto_.variables().size());
284 active_variables_.clear();
285 const int num_variables = model_proto_.variables_size();
286 active_variables_set_.assign(num_variables,
false);
287 for (
int i = 0;
i < num_variables; ++
i) {
288 if (!IsConstant(
i)) {
289 active_variables_.push_back(
i);
290 active_variables_set_[
i] =
true;
294 active_objective_variables_.clear();
295 for (
const int var : model_proto_.objective().vars()) {
297 if (active_variables_set_[
var]) {
298 active_objective_variables_.push_back(
var);
306 for (
int c = 0;
c < constraint_to_var_.size(); ++
c) {
307 const auto row = constraint_to_var_[
c];
308 if (
row.size() <= 1)
continue;
309 for (
int i = 1;
i <
row.size(); ++
i) {
316 if (ObjectiveDomainIsConstraining()) {
317 const auto& refs = model_proto_.objective().vars();
318 const int num_terms = refs.size();
319 for (
int i = 1;
i < num_terms; ++
i) {
330 var_to_component_index_.assign(num_variables, -1);
331 for (
int var = 0;
var < num_variables; ++
var) {
332 if (IsConstant(
var))
continue;
334 DCHECK_LT(root, var_to_component_index_.size());
335 int&
index = var_to_component_index_[root];
337 index = components_.size();
338 components_.push_back({});
340 var_to_component_index_[
var] =
index;
350 std::vector<int> component_sizes;
351 for (
const std::vector<int>& component : components_) {
352 component_sizes.push_back(component.size());
354 std::sort(component_sizes.begin(), component_sizes.end(),
355 std::greater<int>());
356 std::string compo_message;
357 if (component_sizes.size() > 1) {
358 if (component_sizes.size() <= 10) {
360 absl::StrCat(
" compo:", absl::StrJoin(component_sizes,
","));
362 component_sizes.resize(10);
364 absl::StrCat(
" compo:", absl::StrJoin(component_sizes,
","),
",...");
372 "Model", absl::StrCat(
"var:", active_variables_.size(),
"/",
373 num_variables,
" constraints:",
374 simplified_model_proto_->constraints().size(),
"/",
375 model_proto_.constraints().size(), compo_message));
379 return active_variables_set_[
var];
382bool NeighborhoodGeneratorHelper::IsConstant(
int var)
const {
383 const auto& var_proto = model_proto_with_only_variables_->variables(
var);
384 return var_proto.domain_size() == 2 &&
385 var_proto.domain(0) == var_proto.domain(1);
393 absl::ReaderMutexLock lock(&domain_mutex_);
394 *neighborhood.
delta.mutable_variables() =
395 model_proto_with_only_variables_->variables();
406bool NeighborhoodGeneratorHelper::IntervalIsActive(
407 int index,
const CpSolverResponse& initial_solution)
const {
412 if (interval_ct.enforcement_literal().size() == 1) {
413 const int enforcement_ref = interval_ct.enforcement_literal(0);
414 const int enforcement_var =
PositiveRef(enforcement_ref);
415 const int value = initial_solution.solution(enforcement_var);
419 for (
const int v : interval_ct.interval().start().vars()) {
420 if (!IsConstant(v))
return true;
422 for (
const int v : interval_ct.interval().size().vars()) {
423 if (!IsConstant(v))
return true;
425 for (
const int v : interval_ct.interval().end().vars()) {
426 if (!IsConstant(v))
return true;
432 absl::Span<const int> unfiltered_intervals,
433 const CpSolverResponse& initial_solution)
const {
434 std::vector<int> filtered_intervals;
435 filtered_intervals.reserve(unfiltered_intervals.size());
436 absl::ReaderMutexLock lock(&domain_mutex_);
437 for (
const int i : unfiltered_intervals) {
438 if (IntervalIsActive(
i, initial_solution)) filtered_intervals.push_back(
i);
440 return filtered_intervals;
444 const CpSolverResponse& initial_solution)
const {
449std::vector<std::pair<int, int>>
451 const CpSolverResponse& initial_solution)
const {
452 const std::vector<int> active_intervals =
454 const absl::flat_hash_set<int> active_intervals_set(active_intervals.begin(),
455 active_intervals.end());
457 std::vector<std::pair<int, int>> active_rectangles;
459 const NoOverlap2DConstraintProto&
ct =
460 model_proto_.constraints(
ct_index).no_overlap_2d();
461 for (
int i = 0;
i <
ct.x_intervals_size(); ++
i) {
462 const int x_i =
ct.x_intervals(
i);
463 const int y_i =
ct.y_intervals(
i);
464 if (active_intervals_set.contains(x_i) ||
465 active_intervals_set.contains(y_i)) {
466 active_rectangles.push_back({x_i, y_i});
471 return active_rectangles;
474std::vector<std::vector<int>>
476 std::vector<std::vector<int>> intervals_in_constraints;
477 absl::flat_hash_set<std::vector<int>> added_intervals_sets;
478 const auto add_interval_list_only_once =
479 [&intervals_in_constraints,
480 &added_intervals_sets](
const auto& intervals) {
481 std::vector<int> candidate({intervals.begin(), intervals.end()});
483 if (added_intervals_sets.insert(candidate).second) {
484 intervals_in_constraints.push_back(candidate);
488 add_interval_list_only_once(
489 model_proto_.constraints(
ct_index).no_overlap().intervals());
492 add_interval_list_only_once(
493 model_proto_.constraints(
ct_index).cumulative().intervals());
496 add_interval_list_only_once(
497 model_proto_.constraints(
ct_index).no_overlap_2d().x_intervals());
498 add_interval_list_only_once(
499 model_proto_.constraints(
ct_index).no_overlap_2d().y_intervals());
501 return intervals_in_constraints;
506int64_t GetLinearExpressionValue(
const LinearExpressionProto& expr,
507 const CpSolverResponse& initial_solution) {
508 int64_t result = expr.offset();
509 for (
int i = 0;
i < expr.vars_size(); ++
i) {
510 result += expr.coeffs(
i) * initial_solution.solution(expr.vars(
i));
515struct StartEndIndex {
520 bool operator<(
const StartEndIndex& o)
const {
522 std::tie(o.start, o.end, o.noise, o.index_in_input_vector);
526struct TimePartition {
534TimePartition PartitionIndicesAroundRandomTimeWindow(
535 const std::vector<int>& intervals,
const CpModelProto& model_proto,
536 const CpSolverResponse& initial_solution,
double difficulty,
537 absl::BitGenRef random) {
538 std::vector<StartEndIndex> start_end_indices;
541 const ConstraintProto& interval_ct = model_proto.constraints(
interval);
542 const int64_t start_value = GetLinearExpressionValue(
543 interval_ct.interval().start(), initial_solution);
544 const int64_t end_value = GetLinearExpressionValue(
545 interval_ct.interval().end(), initial_solution);
546 start_end_indices.push_back(
547 {start_value, end_value,
index, absl::Uniform(random, 0., 1.0)});
550 if (start_end_indices.empty())
return {};
552 std::sort(start_end_indices.begin(), start_end_indices.end());
553 const int relaxed_size = std::floor(difficulty * start_end_indices.size());
555 std::uniform_int_distribution<int> random_var(
556 0, start_end_indices.size() - relaxed_size - 1);
559 const int random_start_index = random_var(random);
566 std::sort(start_end_indices.begin() + random_start_index,
567 start_end_indices.end(),
568 [](
const StartEndIndex&
a,
const StartEndIndex&
b) {
569 return std::tie(a.end, a.noise, a.index_in_input_vector) <
570 std::tie(b.end, b.noise, b.index_in_input_vector);
572 TimePartition result;
574 for (;
i < random_start_index; ++
i) {
575 result.indices_before_selected.push_back(
578 for (;
i < random_start_index + relaxed_size; ++
i) {
579 result.selected_indices.push_back(
582 for (;
i < start_end_indices.size(); ++
i) {
583 result.indices_after_selected.push_back(
599 bool operator<(
const Demand& other)
const {
601 std::tie(other.start, other.height, other.end);
604 std::string DebugString()
const {
605 return absl::StrCat(
"{i=", interval_index,
" span=[", start,
",", end,
"]",
610void InsertPrecedencesFromSortedListOfNonOverlapingIntervals(
611 const std::vector<Demand>& demands,
612 absl::flat_hash_set<std::pair<int, int>>* precedences) {
613 for (
int i = 0;
i + 1 < demands.size(); ++
i) {
614 DCHECK_LE(demands[
i].
end, demands[
i + 1].
start);
616 {demands[
i].interval_index, demands[
i + 1].interval_index});
620bool IsPresent(
const ConstraintProto& interval_ct,
621 const CpSolverResponse& initial_solution) {
622 if (interval_ct.enforcement_literal().size() != 1)
return true;
624 const int enforcement_ref = interval_ct.enforcement_literal(0);
625 const int enforcement_var =
PositiveRef(enforcement_ref);
626 const int64_t
value = initial_solution.solution(enforcement_var);
630void InsertNoOverlapPrecedences(
631 const absl::flat_hash_set<int>& ignored_intervals,
632 const CpSolverResponse& initial_solution,
const CpModelProto& model_proto,
633 int no_overlap_index,
634 absl::flat_hash_set<std::pair<int, int>>* precedences) {
635 std::vector<Demand> demands;
636 const NoOverlapConstraintProto& no_overlap =
637 model_proto.constraints(no_overlap_index).no_overlap();
640 const ConstraintProto& interval_ct =
642 if (!IsPresent(interval_ct, initial_solution))
continue;
644 const int64_t start_value = GetLinearExpressionValue(
645 interval_ct.interval().start(), initial_solution);
646 const int64_t end_value = GetLinearExpressionValue(
647 interval_ct.interval().end(), initial_solution);
648 DCHECK_LE(start_value, end_value);
654 std::sort(demands.begin(), demands.end());
655 InsertPrecedencesFromSortedListOfNonOverlapingIntervals(demands, precedences);
658void ProcessDemandListFromCumulativeConstraint(
659 const std::vector<Demand>& demands, int64_t capacity,
660 std::deque<std::pair<std::vector<Demand>, int64_t>>* to_process,
661 absl::BitGenRef random,
662 absl::flat_hash_set<std::pair<int, int>>* precedences) {
663 if (demands.size() <= 1)
return;
666 int64_t sum_of_min_two_capacities = 2;
668 int64_t min1 = std::numeric_limits<int64_t>::max();
669 int64_t min2 = std::numeric_limits<int64_t>::max();
670 for (
const Demand&
demand : demands) {
671 if (
demand.height <= min1) {
674 }
else if (
demand.height < min2) {
678 sum_of_min_two_capacities = min1 + min2;
681 DCHECK_GT(sum_of_min_two_capacities, 1);
682 if (sum_of_min_two_capacities > capacity) {
683 InsertPrecedencesFromSortedListOfNonOverlapingIntervals(demands,
688 std::vector<int64_t> unique_starts;
689 for (
const Demand&
demand : demands) {
690 DCHECK(unique_starts.empty() ||
demand.start >= unique_starts.back());
691 if (unique_starts.empty() || unique_starts.back() <
demand.start) {
692 unique_starts.push_back(
demand.start);
695 DCHECK(std::is_sorted(unique_starts.begin(), unique_starts.end()));
696 const int num_points = unique_starts.size();
699 const int64_t capacity1 = capacity / 2;
700 std::vector<int64_t> usage1(num_points);
701 std::vector<Demand> demands1;
703 const int64_t capacity2 = capacity - capacity1;
704 std::vector<int64_t> usage2(num_points);
705 std::vector<Demand> demands2;
708 for (
const Demand& d : demands) {
711 while (usage_index < num_points && unique_starts[usage_index] < d.start) {
714 DCHECK_LT(usage_index, num_points);
715 DCHECK_EQ(unique_starts[usage_index], d.start);
716 const int64_t slack1 = capacity1 - usage1[usage_index];
717 const int64_t slack2 = capacity2 - usage2[usage_index];
724 ? absl::Bernoulli(random, 0.5)
725 : (d.
height <=
std::
min(slack1, slack2) ? slack2 < slack1
728 auto& selected_usage = prefer2 ? usage2 : usage1;
729 auto& residual_usage = prefer2 ? usage1 : usage2;
730 std::vector<Demand>& selected_demands = prefer2 ? demands2 : demands1;
731 std::vector<Demand>& residual_demands = prefer2 ? demands1 : demands2;
732 const int64_t selected_slack = prefer2 ? slack2 : slack1;
734 const int64_t assigned_to_selected = std::min(selected_slack, d.height);
735 DCHECK_GT(assigned_to_selected, 0);
736 for (
int i = usage_index;
i < num_points; ++
i) {
737 if (d.end <= unique_starts[
i])
break;
738 selected_usage[
i] += assigned_to_selected;
740 selected_demands.push_back(
741 {d.interval_index, d.start, d.end, assigned_to_selected});
743 if (d.height > selected_slack) {
744 const int64_t residual = d.height - selected_slack;
745 DCHECK_GT(residual, 0);
746 DCHECK_LE(residual, prefer2 ? slack1 : slack2);
747 for (
int i = usage_index;
i < num_points; ++
i) {
748 if (d.end <= unique_starts[
i])
break;
749 residual_usage[
i] += residual;
751 residual_demands.push_back({d.interval_index, d.start, d.end, residual});
755 if (demands1.size() > 1) {
756 to_process->emplace_back(std::move(demands1), capacity1);
758 if (demands2.size() > 1) {
759 to_process->emplace_back(std::move(demands2), capacity2);
763void InsertCumulativePrecedences(
764 const absl::flat_hash_set<int>& ignored_intervals,
765 const CpSolverResponse& initial_solution,
const CpModelProto& model_proto,
766 int cumulative_index, absl::BitGenRef random,
767 absl::flat_hash_set<std::pair<int, int>>* precedences) {
768 const CumulativeConstraintProto& cumulative =
769 model_proto.constraints(cumulative_index).cumulative();
771 std::vector<Demand> demands;
772 for (
int i = 0;
i < cumulative.intervals().
size(); ++
i) {
775 const ConstraintProto& interval_ct =
777 if (!IsPresent(interval_ct, initial_solution))
continue;
779 const int64_t start_value = GetLinearExpressionValue(
780 interval_ct.interval().start(), initial_solution);
781 const int64_t end_value = GetLinearExpressionValue(
782 interval_ct.interval().end(), initial_solution);
783 const int64_t demand_value =
784 GetLinearExpressionValue(cumulative.demands(
i), initial_solution);
785 if (start_value == end_value || demand_value == 0)
continue;
787 demands.push_back({
interval_index, start_value, end_value, demand_value});
789 std::sort(demands.begin(), demands.end());
791 const int64_t capacity_value =
792 GetLinearExpressionValue(cumulative.capacity(), initial_solution);
793 DCHECK_GT(capacity_value, 0);
796 std::deque<std::pair<std::vector<Demand>, int64_t>> to_process;
797 to_process.emplace_back(std::move(demands), capacity_value);
799 while (!to_process.empty()) {
800 auto& next_task = to_process.front();
801 ProcessDemandListFromCumulativeConstraint(next_task.first,
803 &to_process, random, precedences);
804 to_process.pop_front();
815 bool operator<(
const Rectangle& other)
const {
816 return std::tie(
x_start,
x_end) < std::tie(other.x_start, other.x_end);
820void InsertRectanglePredecences(
821 const std::vector<Rectangle>& rectangles,
822 absl::flat_hash_set<std::pair<int, int>>* precedences) {
824 std::vector<int64_t> interesting_points;
825 for (
const Rectangle& r : rectangles) {
826 interesting_points.push_back(r.y_end - 1);
829 std::vector<Demand> demands;
830 for (
const int64_t t : interesting_points) {
832 for (
const Rectangle& r : rectangles) {
833 if (r.y_start > t || r.y_end <= t)
continue;
834 demands.push_back({r.interval_index, r.x_start, r.x_end, 1});
836 std::sort(demands.begin(), demands.end());
837 InsertPrecedencesFromSortedListOfNonOverlapingIntervals(demands,
842void InsertNoOverlap2dPrecedences(
843 const absl::flat_hash_set<int>& ignored_intervals,
844 const CpSolverResponse& initial_solution,
const CpModelProto& model_proto,
845 int no_overlap_2d_index,
846 absl::flat_hash_set<std::pair<int, int>>* precedences) {
847 std::vector<Demand> demands;
848 const NoOverlap2DConstraintProto& no_overlap_2d =
849 model_proto.constraints(no_overlap_2d_index).no_overlap_2d();
850 std::vector<Rectangle> x_main;
851 std::vector<Rectangle> y_main;
852 for (
int i = 0;
i < no_overlap_2d.x_intervals_size(); ++
i) {
854 const int x_interval_index = no_overlap_2d.x_intervals(
i);
855 if (ignored_intervals.contains(x_interval_index))
continue;
856 const ConstraintProto& x_interval_ct =
857 model_proto.constraints(x_interval_index);
858 if (!IsPresent(x_interval_ct, initial_solution))
continue;
860 const int y_interval_index = no_overlap_2d.y_intervals(
i);
861 if (ignored_intervals.contains(y_interval_index))
continue;
862 const ConstraintProto& y_interval_ct =
863 model_proto.constraints(y_interval_index);
864 if (!IsPresent(y_interval_ct, initial_solution))
continue;
866 const int64_t x_start_value = GetLinearExpressionValue(
867 x_interval_ct.interval().start(), initial_solution);
868 const int64_t x_end_value = GetLinearExpressionValue(
869 x_interval_ct.interval().end(), initial_solution);
870 const int64_t y_start_value = GetLinearExpressionValue(
871 y_interval_ct.interval().start(), initial_solution);
872 const int64_t y_end_value = GetLinearExpressionValue(
873 y_interval_ct.interval().end(), initial_solution);
876 if (x_start_value == x_end_value || y_start_value == y_end_value)
continue;
878 x_main.push_back({x_interval_index, x_start_value, x_end_value,
879 y_start_value, y_end_value});
880 y_main.push_back({y_interval_index, y_start_value, y_end_value,
881 x_start_value, x_end_value});
884 if (x_main.empty() || y_main.empty())
return;
886 std::sort(x_main.begin(), x_main.end());
887 InsertRectanglePredecences(x_main, precedences);
888 std::sort(y_main.begin(), y_main.end());
889 InsertRectanglePredecences(y_main, precedences);
897std::vector<std::pair<int, int>>
899 const absl::flat_hash_set<int>& ignored_intervals,
900 const CpSolverResponse& initial_solution, absl::BitGenRef random)
const {
901 absl::flat_hash_set<std::pair<int, int>> precedences;
903 InsertNoOverlapPrecedences(ignored_intervals, initial_solution,
907 InsertCumulativePrecedences(ignored_intervals, initial_solution,
911 InsertNoOverlap2dPrecedences(ignored_intervals, initial_solution,
916 std::vector<std::pair<int, int>> result(precedences.begin(),
918 std::sort(result.begin(), result.end());
923 const CpSolverResponse& initial_solution)
const {
924 struct HeadAndArcLiteral {
929 std::vector<std::vector<int>> result;
930 absl::flat_hash_map<int, HeadAndArcLiteral> tail_to_head_and_arc_literal;
933 const CircuitConstraintProto&
ct =
ModelProto().constraints(
i).circuit();
936 int min_node = std::numeric_limits<int>::max();
937 tail_to_head_and_arc_literal.clear();
938 for (
int i = 0;
i <
ct.literals_size(); ++
i) {
943 const int64_t
value = initial_solution.solution(bool_var);
948 tail_to_head_and_arc_literal[
tail] = {
head, bool_var};
949 min_node = std::min(
tail, min_node);
951 if (tail_to_head_and_arc_literal.empty())
continue;
954 int current_node = min_node;
955 std::vector<int> path;
957 auto it = tail_to_head_and_arc_literal.find(current_node);
958 CHECK(it != tail_to_head_and_arc_literal.end());
959 current_node = it->second.head;
960 path.push_back(it->second.literal);
961 }
while (current_node != min_node);
962 result.push_back(std::move(path));
965 std::vector<HeadAndArcLiteral> route_starts;
967 const RoutesConstraintProto&
ct =
ModelProto().constraints(
i).routes();
968 tail_to_head_and_arc_literal.clear();
969 route_starts.clear();
972 for (
int i = 0;
i <
ct.literals_size(); ++
i) {
977 const int64_t
value = initial_solution.solution(bool_var);
983 route_starts.push_back({
head, bool_var});
985 tail_to_head_and_arc_literal[
tail] = {
head, bool_var};
990 for (
const HeadAndArcLiteral& head_var : route_starts) {
991 std::vector<int> path;
992 int current_node = head_var.head;
993 path.push_back(head_var.literal);
995 auto it = tail_to_head_and_arc_literal.find(current_node);
996 CHECK(it != tail_to_head_and_arc_literal.end());
997 current_node = it->second.head;
998 path.push_back(it->second.literal);
999 }
while (current_node != 0);
1000 result.push_back(std::move(path));
1008 const CpSolverResponse& base_solution,
1009 const absl::flat_hash_set<int>& variables_to_fix)
const {
1014 const int unique_objective_variable =
1015 model_proto_.has_objective() && model_proto_.objective().vars_size() == 1
1016 ? model_proto_.objective().vars(0)
1021 absl::ReaderMutexLock domain_lock(&domain_mutex_);
1023 const int num_variables =
1024 model_proto_with_only_variables_->variables().size();
1025 neighborhood.
delta.mutable_variables()->Reserve(num_variables);
1026 for (
int i = 0;
i < num_variables; ++
i) {
1027 const IntegerVariableProto& current_var =
1028 model_proto_with_only_variables_->variables(
i);
1029 IntegerVariableProto* new_var = neighborhood.
delta.add_variables();
1032 if (
DEBUG_MODE) new_var->set_name(current_var.name());
1035 const int64_t base_value = base_solution.solution(
i);
1037 if (variables_to_fix.contains(
i) &&
i != unique_objective_variable) {
1039 new_var->add_domain(base_value);
1040 new_var->add_domain(base_value);
1045 int64_t closest_value = domain.
Min();
1046 int64_t closest_dist = std::abs(closest_value - base_value);
1049 const int64_t dist = std::abs(
value - base_value);
1050 if (dist < closest_dist) {
1051 closest_value =
value;
1052 closest_dist = dist;
1069 std::vector<int> count(components_.size(), 0);
1070 const int num_variables = neighborhood.
delta.variables().size();
1071 for (
int var = 0;
var < num_variables; ++
var) {
1072 const auto& domain = neighborhood.
delta.variables(
var).domain();
1073 if (domain.size() != 2 || domain[0] != domain[1]) {
1075 if (is_in_objective_[
var]) {
1078 const int c = var_to_component_index_[
var];
1079 if (c != -1) count[c]++;
1083 for (
int i = 0;
i < components_.size(); ++
i) {
1084 if (count[
i] == components_[
i].
size()) {
1087 components_[
i].begin(), components_[
i].end());
1097 if (model_proto_.has_objective() &&
1098 (model_proto_.objective().domain().size() != 2 ||
1100 model_proto_.objective().domain(0))) {
1107 neighborhood.
is_reduced = !variables_to_fix.empty();
1114 return neighborhood;
1118 const CpSolverResponse& initial_solution, CpModelProto* model_proto)
const {
1120 model_proto->clear_solution_hint();
1121 const auto is_fixed = [model_proto](
int var) {
1122 const IntegerVariableProto& var_proto = model_proto->variables(
var);
1123 return var_proto.domain_size() == 2 &&
1124 var_proto.domain(0) == var_proto.domain(1);
1126 for (
int var = 0;
var < model_proto->variables_size(); ++
var) {
1127 if (is_fixed(
var))
continue;
1129 model_proto->mutable_solution_hint()->add_vars(
var);
1130 model_proto->mutable_solution_hint()->add_values(
1131 initial_solution.solution(
var));
1136 const CpSolverResponse& initial_solution,
1137 const std::vector<int>& relaxed_variables)
const {
1138 std::vector<bool> relaxed_variables_set(model_proto_.variables_size(),
false);
1139 for (
const int var : relaxed_variables) relaxed_variables_set[
var] =
true;
1140 absl::flat_hash_set<int> fixed_variables;
1143 for (
const int i : active_variables_) {
1144 if (!relaxed_variables_set[
i]) {
1145 fixed_variables.insert(
i);
1153 const CpSolverResponse& initial_solution)
const {
1155 const absl::flat_hash_set<int> fixed_variables(all_variables.begin(),
1156 all_variables.end());
1161 CpModelProto updated_model = model_proto_;
1163 absl::MutexLock domain_lock(&domain_mutex_);
1164 *updated_model.mutable_variables() =
1165 model_proto_with_only_variables_->variables();
1167 return updated_model;
1176 DCHECK_GE(total_num_calls, num_calls_);
1177 if (num_calls_ <= 10)
return std::numeric_limits<double>::infinity();
1178 return current_average_ + sqrt((2 * log(total_num_calls)) / num_calls_);
1186 std::sort(solve_data_.begin(), solve_data_.end());
1189 int num_fully_solved_in_batch = 0;
1190 int num_not_fully_solved_in_batch = 0;
1192 double total_dtime = 0.0;
1193 for (
const SolveData& data : solve_data_) {
1199 if (data.status == CpSolverStatus::INFEASIBLE ||
1200 data.status == CpSolverStatus::OPTIMAL) {
1201 ++num_fully_solved_calls_;
1202 ++num_fully_solved_in_batch;
1204 ++num_not_fully_solved_in_batch;
1213 const IntegerValue best_objective_improvement = IntegerValue(
CapSub(
1214 data.initial_best_objective.value(), data.new_objective.value()));
1215 if (best_objective_improvement > 0) {
1216 num_consecutive_non_improving_calls_ = 0;
1217 next_time_limit_bump_ = 50;
1219 ++num_consecutive_non_improving_calls_;
1223 if (data.base_objective > data.new_objective) {
1224 ++num_improving_calls_;
1229 const double gain_per_time_unit =
1230 std::max(0.0,
static_cast<double>(best_objective_improvement.value())) /
1231 (1.0 + data.deterministic_time);
1232 if (num_calls_ <= 100) {
1233 current_average_ += (gain_per_time_unit - current_average_) / num_calls_;
1235 current_average_ = 0.9 * current_average_ + 0.1 * gain_per_time_unit;
1238 total_dtime += data.deterministic_time;
1242 difficulty_.
Update(num_not_fully_solved_in_batch,
1243 num_fully_solved_in_batch);
1251 if (num_consecutive_non_improving_calls_ > next_time_limit_bump_) {
1252 next_time_limit_bump_ = num_consecutive_non_improving_calls_ + 50;
1253 deterministic_limit_ *= 1.02;
1257 deterministic_limit_ = std::min(60.0, deterministic_limit_);
1260 solve_data_.clear();
1267void GetRandomSubset(
double relative_size, std::vector<T>* base,
1268 absl::BitGenRef random) {
1269 if (base->empty())
return;
1273 std::shuffle(base->begin(), base->end(), random);
1274 const int target_size = std::round(relative_size * base->size());
1275 base->resize(target_size);
1281 const CpSolverResponse& initial_solution,
double difficulty,
1282 absl::BitGenRef random) {
1284 GetRandomSubset(1.0 -
difficulty, &fixed_variables, random);
1286 initial_solution, {fixed_variables.begin(), fixed_variables.end()});
1290 const CpSolverResponse& initial_solution,
double difficulty,
1291 absl::BitGenRef random) {
1296 std::vector<int> relaxed_variables;
1300 std::vector<int> active_constraints(num_active_constraints);
1301 for (
int c = 0; c < num_active_constraints; ++c) {
1302 active_constraints[c] = c;
1304 std::shuffle(active_constraints.begin(), active_constraints.end(), random);
1307 std::vector<bool> visited_variables_set(num_model_vars,
false);
1309 const int num_active_vars =
1311 const int target_size = std::ceil(
difficulty * num_active_vars);
1319 if (visited_variables_set[
var])
continue;
1320 visited_variables_set[
var] =
true;
1322 relaxed_variables.push_back(
var);
1323 if (relaxed_variables.size() >= target_size)
break;
1326 if (relaxed_variables.size() >= target_size)
break;
1336 const CpSolverResponse& initial_solution,
double difficulty,
1337 absl::BitGenRef random) {
1339 std::vector<bool> visited_variables_set(num_model_vars,
false);
1340 std::vector<int> relaxed_variables;
1341 std::vector<int> visited_variables;
1345 std::vector<bool> scanned_constraints(num_model_constraints,
false);
1347 std::vector<int> random_variables;
1353 const int num_active_vars =
1355 const int num_objective_variables =
1357 const int target_size = std::ceil(
difficulty * num_active_vars);
1360 const int first_var =
1361 num_objective_variables > 0
1363 [absl::Uniform<int>(random, 0, num_objective_variables)]
1365 random, 0, num_active_vars)];
1366 visited_variables_set[first_var] =
true;
1367 visited_variables.push_back(first_var);
1368 relaxed_variables.push_back(first_var);
1370 for (
int i = 0;
i < visited_variables.size(); ++
i) {
1371 random_variables.clear();
1375 if (scanned_constraints[
ct])
continue;
1376 scanned_constraints[
ct] =
true;
1378 if (visited_variables_set[
var])
continue;
1379 visited_variables_set[
var] =
true;
1380 random_variables.push_back(
var);
1385 std::shuffle(random_variables.begin(), random_variables.end(), random);
1386 for (
const int var : random_variables) {
1387 if (relaxed_variables.size() < target_size) {
1388 visited_variables.push_back(
var);
1390 relaxed_variables.push_back(
var);
1396 if (relaxed_variables.size() >= target_size)
break;
1406 const CpSolverResponse& initial_solution,
double difficulty,
1407 absl::BitGenRef random) {
1417 int num_active_vars = 0;
1418 std::vector<int> active_objective_vars;
1427 const int target_size = std::ceil(
difficulty * num_active_vars);
1431 const int num_objective_variables = active_objective_vars.size();
1433 const int first_var = active_objective_vars[absl::Uniform<int>(
1434 random, 0, num_objective_variables)];
1436 std::vector<bool> relaxed_variables_set(num_model_vars,
false);
1437 std::vector<int> relaxed_variables;
1439 std::vector<int> active_vars;
1441 relaxed_variables_set[first_var] =
true;
1442 relaxed_variables.push_back(first_var);
1443 active_vars.push_back(first_var);
1445 while (relaxed_variables.size() < target_size) {
1446 if (active_vars.empty())
break;
1448 const int tail_index = absl::Uniform<int>(random, 0, active_vars.size());
1449 const int tail_var = active_vars[
tail_index];
1450 int head_var = tail_var;
1451 while (!vars_to_constraints[tail_var].empty() && head_var == tail_var) {
1452 const auto cts = vars_to_constraints[tail_var];
1453 const int pos_ct = absl::Uniform<int>(random, 0, cts.size());
1454 const int ct = cts[pos_ct];
1455 while (!constraints_to_vars[
ct].empty() && head_var == tail_var) {
1456 const auto vars = constraints_to_vars[
ct];
1457 const int pos_var = absl::Uniform<int>(random, 0, vars.size());
1458 const int candidate = vars[pos_var];
1463 if (!relaxed_variables_set[candidate]) {
1464 head_var = candidate;
1467 if (constraints_to_vars[
ct].empty()) {
1474 if (vars_to_constraints[tail_var].empty()) {
1475 std::swap(active_vars[
tail_index], active_vars.back());
1476 active_vars.pop_back();
1479 if (head_var != tail_var) {
1480 relaxed_variables_set[head_var] =
true;
1481 relaxed_variables.push_back(head_var);
1482 active_vars.push_back(head_var);
1491 const CpSolverResponse& initial_solution,
double difficulty,
1492 absl::BitGenRef random) {
1494 if (num_model_constraints == 0) {
1499 std::vector<bool> visited_variables_set(num_model_vars,
false);
1500 std::vector<int> relaxed_variables;
1502 std::vector<bool> added_constraints(num_model_constraints,
false);
1503 std::vector<int> next_constraints;
1505 std::vector<int> random_variables;
1508 const int num_active_vars =
1510 const int target_size = std::ceil(
difficulty * num_active_vars);
1515 if (num_active_constraints != 0) {
1516 next_constraints.push_back(
1517 absl::Uniform<int>(random, 0, num_active_constraints));
1518 added_constraints[next_constraints.back()] =
true;
1521 while (relaxed_variables.size() < target_size) {
1523 if (next_constraints.empty())
break;
1526 const int i = absl::Uniform<int>(random, 0, next_constraints.size());
1528 std::swap(next_constraints[
i], next_constraints.back());
1529 next_constraints.pop_back();
1534 random_variables.assign(
1537 std::shuffle(random_variables.begin(), random_variables.end(), random);
1538 for (
const int var : random_variables) {
1539 if (visited_variables_set[
var])
continue;
1540 visited_variables_set[
var] =
true;
1542 relaxed_variables.push_back(
var);
1544 if (relaxed_variables.size() >= target_size)
break;
1547 if (added_constraints[
ct])
continue;
1548 added_constraints[
ct] =
true;
1549 next_constraints.push_back(
ct);
1559 const CpSolverResponse& initial_solution,
double difficulty,
1560 absl::BitGenRef random) {
1562 int size_at_min_width_after_100;
1563 int min_width_after_100 = std::numeric_limits<int>::max();
1564 int num_zero_score = 0;
1565 std::vector<int> relaxed_variables;
1573 const int num_active_vars =
1575 const int target_size = std::ceil(
difficulty * num_active_vars);
1580 if (num_constraints == 0 || num_vars == 0) {
1586 const int num_nodes = num_vars + num_constraints;
1587 std::vector<bool> added(num_nodes,
false);
1588 std::vector<bool> added_or_connected(num_nodes,
false);
1591 struct QueueElement {
1592 int Index()
const {
return index; }
1593 bool operator<(
const QueueElement& o)
const {
1594 if (score == o.score)
return tie_break < o.tie_break;
1595 return score < o.score;
1600 double tie_break = 0.0;
1602 std::vector<QueueElement> elements(num_nodes);
1606 for (
int i = 0;
i < num_nodes; ++
i) {
1607 elements[
i].index =
i;
1608 elements[
i].tie_break = absl::Uniform<double>(random, 0.0, 1.0);
1618 const int first_index =
1620 random, 0, num_active_vars)];
1622 pq.
Add(elements[first_index]);
1623 added_or_connected[first_index] =
true;
1626 std::vector<int> to_update;
1627 while (!pq.
IsEmpty() && relaxed_variables.size() < target_size) {
1629 if (relaxed_variables.size() > 100 && pq.
Size() < min_width_after_100) {
1630 min_width_after_100 = pq.
Size();
1631 size_at_min_width_after_100 = relaxed_variables.size();
1635 const int score = pq.
Top().score;
1637 added[
index] =
true;
1642 if (
index < num_vars) relaxed_variables.push_back(
index);
1652 if (
index < num_vars) {
1653 relaxed_variables.push_back(
index);
1655 const int c_index = num_vars + c;
1656 if (added_or_connected[c_index])
continue;
1658 added_or_connected[c_index] =
true;
1659 to_update.push_back(c_index);
1661 if (added[v])
continue;
1662 if (added_or_connected[v]) {
1663 to_update.push_back(v);
1664 elements[v].score--;
1666 elements[c_index].score++;
1672 if (added_or_connected[v])
continue;
1674 added_or_connected[v] =
true;
1675 to_update.push_back(v);
1677 if (added[num_vars + c])
continue;
1678 if (added_or_connected[num_vars + c]) {
1679 elements[num_vars + c].score--;
1680 to_update.push_back(num_vars + c);
1682 elements[v].score++;
1691 CHECK_EQ(num_added, score);
1694 for (
const int index : to_update) {
1695 DCHECK(!added[
index]);
1703 max_width = std::max(max_width, pq.
Size());
1707 if (pq.
Size() < min_width_after_100) {
1708 min_width_after_100 = pq.
Size();
1709 size_at_min_width_after_100 = relaxed_variables.size();
1712 VLOG(2) <<
"#relaxed " << relaxed_variables.size() <<
" #zero_score "
1713 << num_zero_score <<
" max_width " << max_width
1714 <<
" (size,min_width)_after_100 (" << size_at_min_width_after_100
1715 <<
"," << min_width_after_100 <<
") " <<
" final_width "
1729ConstraintProto LocalBranchingConstraint(
1730 const std::vector<int>& variable_indices,
1731 const std::vector<int64_t>& initial_solution,
const int neighborhood_size) {
1732 DCHECK_EQ(variable_indices.size(), initial_solution.size());
1733 DCHECK_GE(neighborhood_size, 0);
1734 ConstraintProto local_branching_constraint;
1735 local_branching_constraint.set_name(
"local_branching");
1736 LinearConstraintProto* linear = local_branching_constraint.mutable_linear();
1737 int lhs_constant_value = 0;
1738 for (
int i = 0;
i < variable_indices.size(); ++
i) {
1739 if (initial_solution[
i] == 0) {
1740 linear->add_coeffs(1);
1741 linear->add_vars(variable_indices[
i]);
1743 DCHECK_EQ(initial_solution[
i], 1);
1744 linear->add_coeffs(-1);
1745 linear->add_vars(variable_indices[
i]);
1746 lhs_constant_value++;
1749 linear->add_domain(-lhs_constant_value);
1750 linear->add_domain(-lhs_constant_value + neighborhood_size);
1751 return local_branching_constraint;
1757 const CpSolverResponse& initial_solution,
double difficulty,
1758 absl::BitGenRef random) {
1763 std::vector<int> binary_var_indices;
1764 std::vector<int> non_binary_var_indices;
1765 std::vector<int64_t> binary_var_initial_solution;
1766 for (
const int active_var_index : active_variables) {
1767 const IntegerVariableProto&
var =
1769 if (
var.domain_size() == 2 &&
var.domain(0) == 0 &&
var.domain(1) == 1) {
1770 binary_var_indices.push_back(active_var_index);
1771 binary_var_initial_solution.push_back(
1772 initial_solution.solution(active_var_index));
1774 non_binary_var_indices.push_back(active_var_index);
1777 if (binary_var_indices.empty()) {
1781 const int target_size =
1782 static_cast<int>(std::ceil(
difficulty * binary_var_indices.size()));
1786 *local_branching_model.add_constraints() = LocalBranchingConstraint(
1787 binary_var_indices, binary_var_initial_solution, target_size);
1789 auto*
const params =
model.GetOrCreate<SatParameters>();
1791 params->set_num_workers(1);
1792 params->set_linearization_level(2);
1793 params->set_stop_after_root_propagation(
true);
1794 params->set_add_lp_constraints_lazily(
false);
1796 params->set_cp_model_presolve(
false);
1797 params->set_cp_model_probing_level(0);
1800 params->set_root_lp_iterations(100000);
1801 params->set_max_deterministic_time(10);
1802 if (global_time_limit_ !=
nullptr) {
1805 solve_callback_(local_branching_model, &
model);
1808 const auto lp_constraints =
1811 if (!lp_constraint->HasSolution()) {
1820 std::vector<double> differences;
1821 for (
int i = 0;
i < binary_var_indices.size(); ++
i) {
1823 std::abs(lp_solution->at(var_mapping->Integer(binary_var_indices[
i])) -
1824 binary_var_initial_solution[
i]);
1825 differences.push_back(difference +
1826 absl::Uniform<double>(random, 0.0, 1e-6));
1830 std::vector<int> vars_to_relax(binary_var_indices.size());
1831 absl::c_iota(vars_to_relax, 0);
1832 absl::c_sort(vars_to_relax, [&differences](
const int i,
const int j) {
1833 return differences[
i] > differences[j];
1835 vars_to_relax.resize(target_size);
1839 vars_to_relax.insert(vars_to_relax.end(), non_binary_var_indices.begin(),
1840 non_binary_var_indices.end());
1846void AddPrecedence(
const LinearExpressionProto& before,
1847 const LinearExpressionProto& after, CpModelProto*
model) {
1848 LinearConstraintProto* linear =
model->add_constraints()->mutable_linear();
1849 linear->add_domain(std::numeric_limits<int64_t>::min());
1850 linear->add_domain(after.offset() - before.offset());
1851 for (
int i = 0;
i < before.vars_size(); ++
i) {
1852 linear->add_vars(before.vars(
i));
1853 linear->add_coeffs(before.coeffs(
i));
1855 for (
int i = 0;
i < after.vars_size(); ++
i) {
1856 linear->add_vars(after.vars(
i));
1857 linear->add_coeffs(-after.coeffs(
i));
1864 const absl::Span<
const std::pair<int, int>> precedences,
1865 const CpSolverResponse& initial_solution,
1869 neighborhood.
is_reduced = !precedences.empty();
1873 return neighborhood;
1877 absl::flat_hash_set<int> seen_intervals;
1878 for (
const std::pair<int, int>& prec : precedences) {
1879 seen_intervals.insert(prec.first);
1880 seen_intervals.insert(prec.second);
1884 bool enforcement_literals_fixed =
false;
1886 if (seen_intervals.contains(
i))
continue;
1888 const ConstraintProto& interval_ct = helper.
ModelProto().constraints(
i);
1889 if (interval_ct.enforcement_literal().empty())
continue;
1891 DCHECK_EQ(interval_ct.enforcement_literal().size(), 1);
1892 const int enforcement_ref = interval_ct.enforcement_literal(0);
1893 const int enforcement_var =
PositiveRef(enforcement_ref);
1894 const int value = initial_solution.solution(enforcement_var);
1904 neighborhood.
delta.mutable_variables(enforcement_var)->clear_domain();
1905 neighborhood.
delta.mutable_variables(enforcement_var)->add_domain(
value);
1906 neighborhood.
delta.mutable_variables(enforcement_var)->add_domain(
value);
1907 enforcement_literals_fixed =
true;
1910 for (
const std::pair<int, int>& prec : precedences) {
1911 const LinearExpressionProto& before_end =
1912 helper.
ModelProto().constraints(prec.first).interval().end();
1913 const LinearExpressionProto& after_start =
1914 helper.
ModelProto().constraints(prec.second).interval().start();
1915 DCHECK_LE(GetLinearExpressionValue(before_end, initial_solution),
1916 GetLinearExpressionValue(after_start, initial_solution));
1917 AddPrecedence(before_end, after_start, &neighborhood.
delta);
1924 return neighborhood;
1928 absl::Span<const int> intervals_to_relax,
1929 absl::Span<const int> variables_to_fix,
1930 const CpSolverResponse& initial_solution, absl::BitGenRef random,
1935 absl::flat_hash_set<int> ignored_intervals(intervals_to_relax.begin(),
1936 intervals_to_relax.end());
1941 if (ignored_intervals.contains(
i))
continue;
1943 const ConstraintProto& interval_ct = helper.
ModelProto().constraints(
i);
1944 if (interval_ct.enforcement_literal().empty())
continue;
1946 DCHECK_EQ(interval_ct.enforcement_literal().size(), 1);
1947 const int enforcement_ref = interval_ct.enforcement_literal(0);
1948 const int enforcement_var =
PositiveRef(enforcement_ref);
1949 const int value = initial_solution.solution(enforcement_var);
1957 ignored_intervals.insert(
i);
1962 neighborhood.
delta.mutable_variables(enforcement_var)->clear_domain();
1963 neighborhood.
delta.mutable_variables(enforcement_var)->add_domain(
value);
1964 neighborhood.
delta.mutable_variables(enforcement_var)->add_domain(
value);
1967 if (ignored_intervals.size() >=
1972 return neighborhood;
1981 const std::vector<std::pair<int, int>> precedences =
1984 for (
const std::pair<int, int>& prec : precedences) {
1985 const LinearExpressionProto& before_end =
1986 helper.
ModelProto().constraints(prec.first).interval().end();
1987 const LinearExpressionProto& after_start =
1988 helper.
ModelProto().constraints(prec.second).interval().start();
1989 DCHECK_LE(GetLinearExpressionValue(before_end, initial_solution),
1990 GetLinearExpressionValue(after_start, initial_solution));
1991 AddPrecedence(before_end, after_start, &neighborhood.
delta);
1995 for (
const int var : variables_to_fix) {
1996 const int value = initial_solution.solution(
var);
1997 neighborhood.
delta.mutable_variables(
var)->clear_domain();
1998 neighborhood.
delta.mutable_variables(
var)->add_domain(
value);
1999 neighborhood.
delta.mutable_variables(
var)->add_domain(
value);
2006 return neighborhood;
2010 const CpSolverResponse& initial_solution,
double difficulty,
2011 absl::BitGenRef random) {
2012 std::vector<int> intervals_to_relax =
2014 GetRandomSubset(
difficulty, &intervals_to_relax, random);
2017 intervals_to_relax, {}, initial_solution, random,
helper_);
2021 const CpSolverResponse& initial_solution,
double difficulty,
2022 absl::BitGenRef random) {
2023 std::vector<std::pair<int, int>> precedences =
2025 GetRandomSubset(1.0 -
difficulty, &precedences, random);
2027 precedences, initial_solution,
helper_);
2031void AppendVarsFromAllIntervalIndices(absl::Span<const int> indices,
2032 absl::Span<const int> all_intervals,
2033 const CpModelProto& model_proto,
2034 std::vector<int>* variables) {
2035 for (
const int index : indices) {
2036 const std::vector<int> vars =
2038 variables->insert(variables->end(), vars.begin(), vars.end());
2044 const CpSolverResponse& initial_solution,
double difficulty,
2045 absl::BitGenRef random) {
2046 const std::vector<int> active_intervals =
2051 const TimePartition partition = PartitionIndicesAroundRandomTimeWindow(
2054 std::vector<int> intervals_to_relax;
2055 intervals_to_relax.reserve(partition.selected_indices.size());
2056 std::vector<int> variables_to_fix;
2057 intervals_to_relax.insert(intervals_to_relax.end(),
2058 partition.selected_indices.begin(),
2059 partition.selected_indices.end());
2062 intervals_to_relax.insert(intervals_to_relax.end(),
2063 partition.indices_before_selected.begin(),
2064 partition.indices_before_selected.end());
2065 AppendVarsFromAllIntervalIndices(partition.indices_before_selected,
2073 intervals_to_relax, variables_to_fix, initial_solution, random,
helper_);
2077 const CpSolverResponse& initial_solution,
double difficulty,
2078 absl::BitGenRef random) {
2079 std::vector<int> intervals_to_relax;
2080 std::vector<int> variables_to_fix;
2081 std::vector<int> active_intervals;
2082 for (
const std::vector<int>& intervals : intervals_in_constraints_) {
2084 const TimePartition partition = PartitionIndicesAroundRandomTimeWindow(
2087 intervals_to_relax.insert(intervals_to_relax.end(),
2088 partition.selected_indices.begin(),
2089 partition.selected_indices.end());
2092 intervals_to_relax.insert(intervals_to_relax.end(),
2093 partition.indices_before_selected.begin(),
2094 partition.indices_before_selected.end());
2095 AppendVarsFromAllIntervalIndices(partition.indices_before_selected,
2101 if (intervals_to_relax.empty() && variables_to_fix.empty()) {
2108 intervals_to_relax, variables_to_fix, initial_solution, random,
helper_);
2112 const CpSolverResponse& initial_solution,
double difficulty,
2113 absl::BitGenRef random) {
2114 std::vector<std::pair<int, int>> rectangles_to_freeze =
2116 GetRandomSubset(1.0 -
difficulty, &rectangles_to_freeze, random);
2118 absl::flat_hash_set<int> variables_to_freeze;
2119 for (
const auto& [
x,
y] : rectangles_to_freeze) {
2128 const CpSolverResponse& initial_solution,
double difficulty,
2129 absl::BitGenRef random) {
2130 std::vector<std::pair<int, int>> rectangles_to_relax =
2132 GetRandomSubset(
difficulty, &rectangles_to_relax, random);
2133 std::vector<int> intervals_to_relax;
2134 for (
const auto& [
x,
y] : rectangles_to_relax) {
2135 intervals_to_relax.push_back(
x);
2136 intervals_to_relax.push_back(
y);
2141 intervals_to_relax, {}, initial_solution, random,
helper_);
2145 const CpSolverResponse& initial_solution,
double difficulty,
2146 absl::BitGenRef random) {
2147 const std::vector<std::pair<int, int>> active_rectangles =
2149 const bool use_first_dimension = absl::Bernoulli(random, 0.5);
2150 std::vector<int> projected_intervals;
2151 projected_intervals.reserve(active_rectangles.size());
2152 for (
const auto& [
x,
y] : active_rectangles) {
2153 projected_intervals.push_back(use_first_dimension ?
x :
y);
2156 const TimePartition partition = PartitionIndicesAroundRandomTimeWindow(
2159 std::vector<bool> indices_to_fix(active_rectangles.size(),
true);
2160 for (
const int index : partition.selected_indices) {
2161 indices_to_fix[
index] =
false;
2164 absl::flat_hash_set<int> variables_to_freeze;
2166 if (indices_to_fix[
index]) {
2168 active_rectangles[
index].first,
2169 variables_to_freeze);
2171 active_rectangles[
index].second,
2172 variables_to_freeze);
2180 const CpSolverResponse& initial_solution,
double difficulty,
2181 absl::BitGenRef random) {
2182 const std::vector<std::vector<int>> all_paths =
2186 absl::flat_hash_set<int> all_path_variables;
2187 for (
auto& path : all_paths) {
2188 all_path_variables.insert(path.begin(), path.end());
2190 std::vector<int> fixed_variables(all_path_variables.begin(),
2191 all_path_variables.end());
2192 std::sort(fixed_variables.begin(), fixed_variables.end());
2193 GetRandomSubset(1.0 -
difficulty, &fixed_variables, random);
2195 initial_solution, {fixed_variables.begin(), fixed_variables.end()});
2199 const CpSolverResponse& initial_solution,
double difficulty,
2200 absl::BitGenRef random) {
2201 std::vector<std::vector<int>> all_paths =
2205 absl::flat_hash_set<int> all_path_variables;
2206 for (
const auto& path : all_paths) {
2207 all_path_variables.insert(path.begin(), path.end());
2211 const int num_variables_to_relax =
2212 static_cast<int>(all_path_variables.size() *
difficulty);
2213 absl::flat_hash_set<int> relaxed_variables;
2214 while (relaxed_variables.size() < num_variables_to_relax) {
2215 DCHECK(!all_paths.empty());
2216 const int path_index = absl::Uniform<int>(random, 0, all_paths.size());
2217 std::vector<int>& path = all_paths[path_index];
2218 const int path_size = path.size();
2219 const int segment_length =
2220 std::min(path_size, absl::Uniform<int>(random, 4, 8));
2221 const int segment_start =
2222 absl::Uniform<int>(random, 0, path_size - segment_length);
2223 for (
int i = segment_start;
i < segment_start + segment_length; ++
i) {
2224 relaxed_variables.insert(path[
i]);
2228 path.erase(path.begin() + segment_start,
2229 path.begin() + segment_start + segment_length);
2231 std::swap(all_paths[path_index], all_paths.back());
2232 all_paths.pop_back();
2237 absl::flat_hash_set<int> fixed_variables;
2238 for (
const int var : all_path_variables) {
2239 if (!relaxed_variables.contains(
var)) fixed_variables.insert(
var);
2245 const CpSolverResponse& initial_solution,
double difficulty,
2246 absl::BitGenRef random) {
2247 std::vector<std::vector<int>> all_paths =
2250 if (all_paths.empty()) {
2255 absl::flat_hash_set<int> all_path_variables;
2256 for (
const auto& path : all_paths) {
2257 all_path_variables.insert(path.begin(), path.end());
2261 const int num_variables_to_relax =
2262 static_cast<int>(all_path_variables.size() *
difficulty);
2263 absl::flat_hash_set<int> relaxed_variables;
2266 for (
const auto& path : all_paths) {
2267 relaxed_variables.insert(path.front());
2268 relaxed_variables.insert(path.back());
2272 for (
auto& path : all_paths) {
2273 std::shuffle(path.begin(), path.end(), random);
2277 const int path_to_clean = absl::Uniform<int>(random, 0, all_paths.size());
2278 while (relaxed_variables.size() < num_variables_to_relax &&
2279 !all_paths[path_to_clean].empty()) {
2280 relaxed_variables.insert(all_paths[path_to_clean].back());
2281 all_paths[path_to_clean].pop_back();
2283 if (all_paths[path_to_clean].empty()) {
2284 std::swap(all_paths[path_to_clean], all_paths.back());
2285 all_paths.pop_back();
2289 while (relaxed_variables.size() < num_variables_to_relax) {
2290 DCHECK(!all_paths.empty());
2291 const int path_index = absl::Uniform<int>(random, 0, all_paths.size());
2292 relaxed_variables.insert(all_paths[path_index].back());
2295 all_paths[path_index].pop_back();
2296 if (all_paths[path_index].empty()) {
2297 std::swap(all_paths[path_index], all_paths.back());
2298 all_paths.pop_back();
2303 absl::flat_hash_set<int> fixed_variables;
2304 for (
const int var : all_path_variables) {
2305 if (!relaxed_variables.contains(
var)) fixed_variables.insert(
var);
2316 const CpSolverResponse& ,
double difficulty,
2317 absl::BitGenRef random) {
2327 return neighborhood;
2333 for (
const std::pair</*model_var*/ int, /*value*/ int64_t>& fixed_var :
2335 const int var = fixed_var.first;
2336 const int64_t
value = fixed_var.second;
2337 if (
var >= neighborhood.
delta.variables_size())
continue;
2342 return neighborhood;
2345 neighborhood.
delta.mutable_variables(
var)->clear_domain();
2346 neighborhood.
delta.mutable_variables(
var)->add_domain(
value);
2347 neighborhood.
delta.mutable_variables(
var)->add_domain(
value);
2351 for (
const std::pair<
int,
2352 std::pair<int64_t, int64_t>>& reduced_var :
2354 const int var = reduced_var.first;
2355 const int64_t lb = reduced_var.second.first;
2356 const int64_t ub = reduced_var.second.second;
2357 if (
var >= neighborhood.
delta.variables_size())
continue;
2370 return neighborhood;
A connected components finder that only works on dense ints.
bool AddEdge(int node1, int node2)
void SetNumberOfNodes(int num_nodes)
void Update(int num_decreases, int num_increases)
static Domain FromValues(std::vector< int64_t > values)
Domain IntersectionWith(const Domain &domain) const
bool Contains(int64_t value) 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.
void UpdateLocalLimit(TimeLimit *local_limit)
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
void RemoveBySwap(K key, int index)
size_t size() const
Size of the "key" space, always in [0, size()).
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
A class that stores the collection of all LP constraints in a model.
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, 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.
Neighborhood RelaxGivenVariables(const CpSolverResponse &initial_solution, const std::vector< int > &relaxed_variables) const
Neighborhood FixAllVariables(const CpSolverResponse &initial_solution) const
Neighborhood FixGivenVariables(const CpSolverResponse &base_solution, const absl::flat_hash_set< int > &variables_to_fix) const
Neighborhood NoNeighborhood() const
const std::vector< int > & ActiveVariablesWhileHoldingLock() const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_)
const CompactVectorVector< int, int > & VarToConstraint() const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_)
std::vector< std::pair< int, int > > GetActiveRectangles(const CpSolverResponse &initial_solution) const
const SharedResponseManager & shared_response() const
void Synchronize() override
NeighborhoodGeneratorHelper(CpModelProto const *model_proto, SatParameters const *parameters, SharedResponseManager *shared_response, SharedBoundsManager *shared_bounds=nullptr)
std::vector< int > ActiveVariables() const
Returns the list of "active" variables.
std::vector< std::vector< int > > GetUniqueIntervalSets() const
const CompactVectorVector< int, int > & ConstraintToVar() const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_)
bool DifficultyMeansFullNeighborhood(double difficulty) const
const CpModelProto & ModelProto() const
void AddSolutionHinting(const CpSolverResponse &initial_solution, CpModelProto *model_proto) const
std::vector< std::vector< int > > GetRoutingPaths(const CpSolverResponse &initial_solution) const
std::vector< int > ActiveObjectiveVariablesWhileHoldingLock() const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_)
const SatParameters & Parameters() 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
virtual bool ReadyToGenerate() const
Returns true if the neighborhood generator can generate a neighborhood.
absl::Mutex generator_mutex_
double difficulty() const
The current difficulty of this generator.
double GetUCBScore(int64_t total_num_calls) const
const NeighborhoodGeneratorHelper & helper_
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, 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, double difficulty, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
void GetChangedBounds(int id, std::vector< int > *variables, std::vector< int64_t > *new_lower_bounds, std::vector< int64_t > *new_upper_bounds)
IntegerValue GetInnerObjectiveLowerBound()
void LogMessageWithThrottling(const std::string &prefix, const std::string &message)
bool LoggingIsEnabled() const
const SharedSolutionRepository< int64_t > & SolutionsRepository() const
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
SubsolverType type() const
Returns the type of the subsolver.
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
int index_in_input_vector
std::vector< int > indices_after_selected
std::vector< int > indices_before_selected
std::vector< int > selected_indices
GurobiMPCallbackContext * context
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
bool RefIsPositive(int ref)
ReducedDomainNeighborhood GetRinsRensNeighborhood(const SharedResponseManager *response_manager, const SharedLPSolutionRepository *lp_solutions, SharedIncompleteSolutionManager *incomplete_solutions, double difficulty, absl::BitGenRef random)
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)
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)
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.
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Reads a Domain from the domain field of a proto.
void InsertVariablesFromConstraint(const CpModelProto &model_proto, int index, Set &output)
Insert variables in a constraint into a set.
In SWIG mode, we don't want anything besides these top-level includes.
int64_t CapSub(int64_t x, int64_t y)
std::optional< int64_t > end
Adds solve data about one "solved" neighborhood.
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
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.