22#include "absl/container/flat_hash_set.h"
23#include "absl/strings/str_join.h"
24#include "absl/synchronization/mutex.h"
46 shared_time_limit_(shared_time_limit),
47 shared_bounds_(shared_bounds),
48 shared_response_(shared_response) {
49 CHECK(shared_response_ !=
nullptr);
50 if (shared_bounds_ !=
nullptr) {
53 *model_proto_with_only_variables_.mutable_variables() =
55 InitializeHelperData();
56 RecomputeHelperData();
61 if (shared_bounds_ !=
nullptr) {
62 std::vector<int> model_variables;
63 std::vector<int64_t> new_lower_bounds;
64 std::vector<int64_t> new_upper_bounds;
66 &new_lower_bounds, &new_upper_bounds);
68 bool new_variables_have_been_fixed =
false;
71 absl::MutexLock domain_lock(&domain_mutex_);
73 for (
int i = 0; i < model_variables.size(); ++i) {
74 const int var = model_variables[i];
75 const int64_t new_lb = new_lower_bounds[i];
76 const int64_t new_ub = new_upper_bounds[i];
79 model_proto_with_only_variables_.variables(
var).domain();
80 const int64_t old_lb = domain.Get(0);
81 const int64_t old_ub = domain.Get(domain.size() - 1);
82 VLOG(3) <<
"Variable: " <<
var <<
" old domain: [" << old_lb <<
", "
83 << old_ub <<
"] new domain: [" << new_lb <<
", " << new_ub
87 model_proto_with_only_variables_.variables(
var));
108 model_proto_with_only_variables_.mutable_variables(
var));
109 new_variables_have_been_fixed |= new_domain.
IsFixed();
114 if (new_variables_have_been_fixed) {
115 RecomputeHelperData();
120bool NeighborhoodGeneratorHelper::ObjectiveDomainIsConstraining()
const {
124 int64_t min_activity = 0;
125 int64_t max_activity = 0;
126 const int num_terms = model_proto_.
objective().
vars().size();
127 for (
int i = 0; i < num_terms; ++i) {
130 const auto& var_domain =
131 model_proto_with_only_variables_.variables(
var).domain();
132 const int64_t v1 =
coeff * var_domain[0];
133 const int64_t v2 =
coeff * var_domain[var_domain.size() - 1];
139 const Domain inferred_domain =
140 Domain(min_activity, max_activity)
146void NeighborhoodGeneratorHelper::InitializeHelperData() {
147 type_to_constraints_.clear();
149 for (
int c = 0; c < num_constraints; ++c) {
151 if (type >= type_to_constraints_.size()) {
152 type_to_constraints_.resize(type + 1);
154 type_to_constraints_[type].push_back(c);
157 const int num_variables = model_proto_.
variables().size();
158 is_in_objective_.resize(num_variables,
false);
168void NeighborhoodGeneratorHelper::RecomputeHelperData() {
170 absl::ReaderMutexLock domain_lock(&domain_mutex_);
184 CpModelProto mapping_proto;
185 simplied_model_proto_.Clear();
186 *simplied_model_proto_.mutable_variables() =
187 model_proto_with_only_variables_.variables();
188 PresolveContext
context(&local_model, &simplied_model_proto_,
194 copier.ImportAndSimplifyConstraints(model_proto_, {});
200 const auto& constraints = simplied_model_proto_.constraints();
202 constraint_to_var_.assign(constraints.size(), {});
203 int reduced_ct_index = 0;
204 for (
int ct_index = 0; ct_index < constraints.size(); ++ct_index) {
212 if (IsConstant(
var))
continue;
213 constraint_to_var_[reduced_ct_index].push_back(
var);
220 if (IsConstant(
var))
continue;
221 constraint_to_var_[reduced_ct_index].push_back(
var);
227 if (constraint_to_var_[reduced_ct_index].size() <= 1) {
228 constraint_to_var_[reduced_ct_index].clear();
233 for (
const int var : constraint_to_var_[reduced_ct_index]) {
234 var_to_constraint_[
var].push_back(reduced_ct_index);
238 constraint_to_var_.resize(reduced_ct_index);
242 active_variables_.clear();
244 active_variables_set_.assign(num_variables,
false);
245 for (
int i = 0; i < num_variables; ++i) {
246 if (!IsConstant(i)) {
247 active_variables_.push_back(i);
248 active_variables_set_[i] =
true;
256 for (
const std::vector<int>& var_in_constraint : constraint_to_var_) {
257 if (var_in_constraint.size() <= 1)
continue;
258 for (
int i = 1; i < var_in_constraint.size(); ++i) {
259 union_find.
AddEdge(var_in_constraint[0], var_in_constraint[i]);
265 if (ObjectiveDomainIsConstraining()) {
267 const int num_terms = refs.size();
268 for (
int i = 1; i < num_terms; ++i) {
279 var_to_component_index_.assign(num_variables, -1);
280 for (
int var = 0;
var < num_variables; ++
var) {
281 if (IsConstant(
var))
continue;
283 CHECK_LT(root, var_to_component_index_.size());
284 int&
index = var_to_component_index_[root];
286 index = components_.size();
287 components_.push_back({});
289 var_to_component_index_[
var] =
index;
297 std::vector<int> component_sizes;
298 for (
const std::vector<int>& component : components_) {
299 component_sizes.push_back(component.size());
301 std::sort(component_sizes.begin(), component_sizes.end(),
302 std::greater<int>());
303 std::string compo_message;
304 if (component_sizes.size() > 1) {
305 if (component_sizes.size() <= 10) {
307 absl::StrCat(
" compo:", absl::StrJoin(component_sizes,
","));
309 component_sizes.resize(10);
311 absl::StrCat(
" compo:", absl::StrJoin(component_sizes,
","),
",...");
315 absl::StrCat(
"var:", active_variables_.size(),
"/", num_variables,
316 " constraints:", simplied_model_proto_.constraints().size(),
317 "/", model_proto_.
constraints().size(), compo_message));
321 return active_variables_set_[
var];
324bool NeighborhoodGeneratorHelper::IsConstant(
int var)
const {
325 return model_proto_with_only_variables_.variables(
var).domain_size() == 2 &&
326 model_proto_with_only_variables_.variables(
var).domain(0) ==
327 model_proto_with_only_variables_.variables(
var).domain(1);
335 absl::ReaderMutexLock lock(&domain_mutex_);
337 model_proto_with_only_variables_.variables();
350 std::vector<int> active_intervals;
351 absl::ReaderMutexLock lock(&domain_mutex_);
359 const int enforcement_var =
PositiveRef(enforcement_ref);
360 const int value = initial_solution.
solution(enforcement_var);
369 bool is_constant =
true;
371 if (!IsConstant(v)) {
377 if (!IsConstant(v)) {
383 if (!IsConstant(v)) {
388 if (is_constant)
continue;
391 active_intervals.push_back(i);
393 return active_intervals;
398 struct HeadAndArcLiteral {
403 std::vector<std::vector<int>> result;
404 absl::flat_hash_map<int, HeadAndArcLiteral> tail_to_head_and_arc_literal;
411 tail_to_head_and_arc_literal.clear();
412 for (
int i = 0; i <
ct.literals_size(); ++i) {
414 const int head =
ct.heads(i);
415 const int tail =
ct.tails(i);
422 tail_to_head_and_arc_literal[
tail] = {
head, bool_var};
425 if (tail_to_head_and_arc_literal.empty())
continue;
428 int current_node = min_node;
429 std::vector<int> path;
431 auto it = tail_to_head_and_arc_literal.find(current_node);
432 CHECK(it != tail_to_head_and_arc_literal.end());
433 current_node = it->second.head;
434 path.push_back(it->second.literal);
435 }
while (current_node != min_node);
436 result.push_back(std::move(path));
439 std::vector<HeadAndArcLiteral> route_starts;
442 tail_to_head_and_arc_literal.clear();
443 route_starts.clear();
446 for (
int i = 0; i <
ct.literals_size(); ++i) {
448 const int head =
ct.heads(i);
449 const int tail =
ct.tails(i);
457 route_starts.push_back({
head, bool_var});
459 tail_to_head_and_arc_literal[
tail] = {
head, bool_var};
464 for (
const HeadAndArcLiteral& head_var : route_starts) {
465 std::vector<int> path;
466 int current_node = head_var.head;
467 path.push_back(head_var.literal);
469 auto it = tail_to_head_and_arc_literal.find(current_node);
470 CHECK(it != tail_to_head_and_arc_literal.end());
471 current_node = it->second.head;
472 path.push_back(it->second.literal);
473 }
while (current_node != 0);
474 result.push_back(std::move(path));
483 const absl::flat_hash_set<int>& variables_to_fix)
const {
488 absl::ReaderMutexLock domain_lock(&domain_mutex_);
490 const int num_variables =
491 model_proto_with_only_variables_.variables().size();
493 for (
int i = 0; i < num_variables; ++i) {
495 model_proto_with_only_variables_.variables(i);
502 const int64_t base_value = base_solution.
solution(i);
518 }
else if (variables_to_fix.contains(i)) {
532 std::vector<int> count(components_.size(), 0);
534 for (
int var = 0;
var < num_variables; ++
var) {
536 if (domain.size() != 2 || domain[0] != domain[1]) {
538 if (is_in_objective_[
var]) {
541 const int c = var_to_component_index_[
var];
542 if (c != -1) count[c]++;
546 for (
int i = 0; i < components_.size(); ++i) {
547 if (count[i] == components_[i].size()) {
550 components_[i].begin(), components_[i].end());
570 neighborhood.
is_reduced = !variables_to_fix.empty();
590 if (is_fixed(
var))
continue;
599 const std::vector<int>& constraints_to_remove)
const {
602 if (constraints_to_remove.empty())
return neighborhood;
610 const std::vector<int>& relaxed_variables)
const {
611 std::vector<bool> relaxed_variables_set(model_proto_.
variables_size(),
false);
612 for (
const int var : relaxed_variables) relaxed_variables_set[
var] =
true;
613 absl::flat_hash_set<int> fixed_variables;
616 for (
const int i : active_variables_) {
617 if (!relaxed_variables_set[i]) {
618 fixed_variables.insert(i);
628 const absl::flat_hash_set<int> fixed_variables(all_variables.begin(),
629 all_variables.end());
640 if (num_calls_ <= 10)
return std::numeric_limits<double>::infinity();
641 return current_average_ + sqrt((2 * log(total_num_calls)) / num_calls_);
649 std::sort(solve_data_.begin(), solve_data_.end());
652 int num_fully_solved_in_batch = 0;
653 int num_not_fully_solved_in_batch = 0;
655 for (
const SolveData& data : solve_data_) {
664 ++num_fully_solved_calls_;
665 ++num_fully_solved_in_batch;
667 ++num_not_fully_solved_in_batch;
676 const IntegerValue best_objective_improvement =
678 ? IntegerValue(
CapSub(data.new_objective_bound.value(),
679 data.initial_best_objective_bound.value()))
680 : IntegerValue(
CapSub(data.initial_best_objective.value(),
681 data.new_objective.value()));
682 if (best_objective_improvement > 0) {
683 num_consecutive_non_improving_calls_ = 0;
685 ++num_consecutive_non_improving_calls_;
690 const double gain_per_time_unit =
691 std::max(0.0,
static_cast<double>(best_objective_improvement.value())) /
692 (1.0 + data.deterministic_time);
693 if (num_calls_ <= 100) {
694 current_average_ += (gain_per_time_unit - current_average_) / num_calls_;
696 current_average_ = 0.9 * current_average_ + 0.1 * gain_per_time_unit;
699 deterministic_time_ += data.deterministic_time;
703 difficulty_.
Update(num_not_fully_solved_in_batch,
704 num_fully_solved_in_batch);
712 if (num_consecutive_non_improving_calls_ > 50) {
713 num_consecutive_non_improving_calls_ = 0;
714 deterministic_limit_ *= 1.02;
718 deterministic_limit_ =
std::min(60.0, deterministic_limit_);
726void GetRandomSubset(
double relative_size, std::vector<int>* base,
727 absl::BitGenRef random) {
728 if (base->empty())
return;
732 std::shuffle(base->begin(), base->end(), random);
733 const int target_size = std::round(relative_size * base->size());
734 base->resize(target_size);
741 absl::BitGenRef random) {
743 GetRandomSubset(1.0 -
difficulty, &fixed_variables, random);
745 initial_solution, {fixed_variables.begin(), fixed_variables.end()});
750 absl::BitGenRef random) {
755 std::vector<int> relaxed_variables;
759 std::vector<int> active_constraints(num_active_constraints);
760 for (
int c = 0; c < num_active_constraints; ++c) {
761 active_constraints[c] = c;
763 std::shuffle(active_constraints.begin(), active_constraints.end(), random);
766 std::vector<bool> visited_variables_set(num_model_vars,
false);
768 const int num_active_vars =
770 const int target_size = std::ceil(
difficulty * num_active_vars);
773 for (
const int constraint_index : active_constraints) {
775 if (visited_variables_set[
var])
continue;
776 visited_variables_set[
var] =
true;
778 relaxed_variables.push_back(
var);
779 if (relaxed_variables.size() == target_size)
break;
782 if (relaxed_variables.size() == target_size)
break;
791 absl::BitGenRef random) {
797 std::vector<bool> visited_variables_set(num_model_vars,
false);
798 std::vector<int> relaxed_variables;
799 std::vector<int> visited_variables;
803 std::vector<bool> scanned_constraints(num_model_constraints,
false);
805 std::vector<int> random_variables;
811 const int num_active_vars =
813 const int target_size = std::ceil(
difficulty * num_active_vars);
816 const int first_var =
818 random, 0, num_active_vars)];
820 visited_variables_set[first_var] =
true;
821 visited_variables.push_back(first_var);
822 relaxed_variables.push_back(first_var);
824 for (
int i = 0; i < visited_variables.size(); ++i) {
825 random_variables.clear();
829 if (scanned_constraints[
ct])
continue;
830 scanned_constraints[
ct] =
true;
832 if (visited_variables_set[
var])
continue;
833 visited_variables_set[
var] =
true;
834 random_variables.push_back(
var);
839 std::shuffle(random_variables.begin(), random_variables.end(), random);
840 for (
const int var : random_variables) {
841 if (relaxed_variables.size() < target_size) {
842 visited_variables.push_back(
var);
844 relaxed_variables.push_back(
var);
850 if (relaxed_variables.size() >= target_size)
break;
858 absl::BitGenRef random) {
860 if (num_model_constraints == 0 ||
866 std::vector<bool> visited_variables_set(num_model_vars,
false);
867 std::vector<int> relaxed_variables;
869 std::vector<bool> added_constraints(num_model_constraints,
false);
870 std::vector<int> next_constraints;
872 std::vector<int> random_variables;
875 const int num_active_vars =
877 const int target_size = std::ceil(
difficulty * num_active_vars);
882 if (num_active_constraints != 0) {
883 next_constraints.push_back(
884 absl::Uniform<int>(random, 0, num_active_constraints));
885 added_constraints[next_constraints.back()] =
true;
888 while (relaxed_variables.size() < target_size) {
890 if (next_constraints.empty())
break;
893 const int i = absl::Uniform<int>(random, 0, next_constraints.size());
894 const int constraint_index = next_constraints[i];
895 std::swap(next_constraints[i], next_constraints.back());
896 next_constraints.pop_back();
900 CHECK_LT(constraint_index, num_active_constraints);
902 std::shuffle(random_variables.begin(), random_variables.end(), random);
903 for (
const int var : random_variables) {
904 if (visited_variables_set[
var])
continue;
905 visited_variables_set[
var] =
true;
907 relaxed_variables.push_back(
var);
909 if (relaxed_variables.size() == target_size)
break;
912 if (added_constraints[
ct])
continue;
913 added_constraints[
ct] =
true;
914 next_constraints.push_back(
ct);
926 int64_t result = expr.
offset();
927 for (
int i = 0; i < expr.
vars_size(); ++i) {
933void AddLinearExpressionToConstraint(
const int64_t
coeff,
934 const LinearExpressionProto& expr,
935 LinearConstraintProto* constraint,
936 int64_t* rhs_offset) {
937 *rhs_offset -=
coeff * expr.offset();
938 for (
int i = 0; i < expr.vars_size(); ++i) {
939 constraint->add_vars(expr.vars(i));
940 constraint->add_coeffs(expr.coeffs(i) *
coeff);
944void AddPrecedenceConstraints(
const absl::Span<const int> intervals,
945 const absl::flat_hash_set<int>& ignored_intervals,
946 const CpSolverResponse& initial_solution,
947 const NeighborhoodGeneratorHelper& helper,
948 Neighborhood* neighborhood) {
951 std::vector<std::pair<int64_t, int>> start_interval_pairs;
952 for (
const int i : intervals) {
953 if (ignored_intervals.contains(i))
continue;
954 const ConstraintProto& interval_ct = helper.ModelProto().constraints(i);
957 const LinearExpressionProto& size_var = interval_ct.interval().size();
958 if (GetLinearExpressionValue(size_var, initial_solution) == 0)
continue;
960 const LinearExpressionProto& start_var = interval_ct.interval().start();
961 const int64_t start_value =
962 GetLinearExpressionValue(start_var, initial_solution);
964 start_interval_pairs.push_back({start_value, i});
966 std::sort(start_interval_pairs.begin(), start_interval_pairs.end());
969 for (
int i = 0; i + 1 < start_interval_pairs.size(); ++i) {
970 const LinearExpressionProto& before_start =
972 .constraints(start_interval_pairs[i].second)
975 const LinearExpressionProto& before_end =
977 .constraints(start_interval_pairs[i].second)
980 const LinearExpressionProto& after_start =
982 .constraints(start_interval_pairs[i + 1].second)
988 LinearConstraintProto* linear =
989 neighborhood->delta.add_constraints()->mutable_linear();
991 int64_t rhs_offset = 0;
992 if (GetLinearExpressionValue(before_end, initial_solution) <=
993 GetLinearExpressionValue(after_start, initial_solution)) {
995 AddLinearExpressionToConstraint(1, before_end, linear, &rhs_offset);
999 rhs_offset = GetLinearExpressionValue(before_start, initial_solution) -
1000 GetLinearExpressionValue(after_start, initial_solution);
1001 AddLinearExpressionToConstraint(1, before_start, linear, &rhs_offset);
1004 AddLinearExpressionToConstraint(-1, after_start, linear, &rhs_offset);
1005 linear->add_domain(rhs_offset);
1009 int64_t activity = 0;
1010 for (
int i = 0; i < linear->vars().size(); ++i) {
1012 linear->coeffs(i) * initial_solution.solution(linear->vars(i));
1014 CHECK_GE(activity, linear->domain(0));
1015 CHECK_LE(activity, linear->domain(1));
1022 const absl::Span<const int> intervals_to_relax,
1027 (intervals_to_relax.size() <
1031 absl::flat_hash_set<int> ignored_intervals(intervals_to_relax.begin(),
1032 intervals_to_relax.end());
1037 if (ignored_intervals.contains(i))
continue;
1044 const int enforcement_var =
PositiveRef(enforcement_ref);
1045 const int value = initial_solution.
solution(enforcement_var);
1053 ignored_intervals.insert(i);
1064 AddPrecedenceConstraints(
1066 ignored_intervals, initial_solution, helper, &neighborhood);
1069 AddPrecedenceConstraints(
1071 ignored_intervals, initial_solution, helper, &neighborhood);
1074 AddPrecedenceConstraints(
1076 ignored_intervals, initial_solution, helper, &neighborhood);
1077 AddPrecedenceConstraints(
1079 ignored_intervals, initial_solution, helper, &neighborhood);
1086 return neighborhood;
1091 absl::BitGenRef random) {
1092 std::vector<int> intervals_to_relax =
1094 GetRandomSubset(
difficulty, &intervals_to_relax, random);
1102 absl::BitGenRef random) {
1103 std::vector<std::pair<int64_t, int>> start_interval_pairs;
1104 const std::vector<int> active_intervals =
1106 std::vector<int> intervals_to_relax;
1110 for (
const int i : active_intervals) {
1113 const int64_t start_value =
1114 GetLinearExpressionValue(start_var, initial_solution);
1115 start_interval_pairs.push_back({start_value, i});
1117 std::sort(start_interval_pairs.begin(), start_interval_pairs.end());
1118 const int relaxed_size = std::floor(
difficulty * start_interval_pairs.size());
1120 std::uniform_int_distribution<int> random_var(
1121 0, start_interval_pairs.size() - relaxed_size - 1);
1122 const int random_start_index = random_var(random);
1126 for (
int i = random_start_index; i < relaxed_size; ++i) {
1127 intervals_to_relax.push_back(start_interval_pairs[i].second);
1136 absl::BitGenRef random) {
1137 const std::vector<std::vector<int>> all_paths =
1141 absl::flat_hash_set<int> all_path_variables;
1142 for (
auto& path : all_paths) {
1143 all_path_variables.insert(path.begin(), path.end());
1145 std::vector<int> fixed_variables(all_path_variables.begin(),
1146 all_path_variables.end());
1147 std::sort(fixed_variables.begin(), fixed_variables.end());
1148 GetRandomSubset(1.0 -
difficulty, &fixed_variables, random);
1150 initial_solution, {fixed_variables.begin(), fixed_variables.end()});
1155 absl::BitGenRef random) {
1156 std::vector<std::vector<int>> all_paths =
1160 absl::flat_hash_set<int> all_path_variables;
1161 for (
const auto& path : all_paths) {
1162 all_path_variables.insert(path.begin(), path.end());
1166 const int num_variables_to_relax =
1167 static_cast<int>(all_path_variables.size() *
difficulty);
1168 absl::flat_hash_set<int> relaxed_variables;
1169 while (relaxed_variables.size() < num_variables_to_relax) {
1170 DCHECK(!all_paths.empty());
1171 const int path_index = absl::Uniform<int>(random, 0, all_paths.size());
1172 std::vector<int>& path = all_paths[path_index];
1173 const int path_size = path.size();
1174 const int segment_length =
1175 std::min(path_size, absl::Uniform<int>(random, 4, 8));
1176 const int segment_start =
1177 absl::Uniform<int>(random, 0, path_size - segment_length);
1178 for (
int i = segment_start; i < segment_start + segment_length; ++i) {
1179 relaxed_variables.insert(path[i]);
1183 path.erase(path.begin() + segment_start,
1184 path.begin() + segment_start + segment_length);
1186 std::swap(all_paths[path_index], all_paths.back());
1187 all_paths.pop_back();
1192 absl::flat_hash_set<int> fixed_variables;
1193 for (
const int var : all_path_variables) {
1194 if (!relaxed_variables.contains(
var)) fixed_variables.insert(
var);
1201 absl::BitGenRef random) {
1202 std::vector<std::vector<int>> all_paths =
1205 if (all_paths.empty()) {
1210 absl::flat_hash_set<int> all_path_variables;
1211 for (
const auto& path : all_paths) {
1212 all_path_variables.insert(path.begin(), path.end());
1216 const int num_variables_to_relax =
1217 static_cast<int>(all_path_variables.size() *
difficulty);
1218 absl::flat_hash_set<int> relaxed_variables;
1221 for (
const auto& path : all_paths) {
1222 relaxed_variables.insert(path.front());
1223 relaxed_variables.insert(path.back());
1227 for (
auto& path : all_paths) {
1228 std::shuffle(path.begin(), path.end(), random);
1232 const int path_to_clean = absl::Uniform<int>(random, 0, all_paths.size());
1233 while (relaxed_variables.size() < num_variables_to_relax &&
1234 !all_paths[path_to_clean].empty()) {
1235 relaxed_variables.insert(all_paths[path_to_clean].back());
1236 all_paths[path_to_clean].pop_back();
1238 if (all_paths[path_to_clean].empty()) {
1239 std::swap(all_paths[path_to_clean], all_paths.back());
1240 all_paths.pop_back();
1244 while (relaxed_variables.size() < num_variables_to_relax) {
1245 DCHECK(!all_paths.empty());
1246 const int path_index = absl::Uniform<int>(random, 0, all_paths.size());
1247 relaxed_variables.insert(all_paths[path_index].back());
1250 all_paths[path_index].pop_back();
1251 if (all_paths[path_index].empty()) {
1252 std::swap(all_paths[path_index], all_paths.back());
1253 all_paths.pop_back();
1258 absl::flat_hash_set<int> fixed_variables;
1259 for (
const int var : all_path_variables) {
1260 if (!relaxed_variables.contains(
var)) fixed_variables.insert(
var);
1266 if (incomplete_solutions_ !=
nullptr) {
1270 if (response_manager_ !=
nullptr) {
1278 if (lp_solutions_ !=
nullptr && lp_solutions_->
NumSolutions() > 0) {
1282 if (relaxation_solutions_ !=
nullptr &&
1291 absl::BitGenRef random) {
1295 const bool lp_solution_available =
1296 (lp_solutions_ !=
nullptr && lp_solutions_->
NumSolutions() > 0);
1298 const bool relaxation_solution_available =
1299 (relaxation_solutions_ !=
nullptr &&
1302 const bool incomplete_solution_available =
1303 (incomplete_solutions_ !=
nullptr &&
1306 if (!lp_solution_available && !relaxation_solution_available &&
1307 !incomplete_solution_available) {
1308 return neighborhood;
1315 std::bernoulli_distribution random_bool(0.5);
1316 const bool use_lp_relaxation =
1317 (lp_solution_available && relaxation_solution_available)
1318 ? random_bool(random)
1319 : lp_solution_available;
1320 if (use_lp_relaxation) {
1323 nullptr, lp_solutions_,
1324 incomplete_solutions_, random);
1326 incomplete_solution_available ?
"incomplete" :
"lp";
1328 CHECK(relaxation_solution_available || incomplete_solution_available);
1330 response_manager_, relaxation_solutions_,
1331 nullptr, incomplete_solutions_, random);
1333 incomplete_solution_available ?
"incomplete" :
"relaxation";
1338 return neighborhood;
1343 for (
const std::pair</*model_var*/ int, /*value*/ int64_t> fixed_var :
1345 const int var = fixed_var.first;
1346 const int64_t
value = fixed_var.second;
1352 return neighborhood;
1361 for (
const std::pair<
int,
1362 std::pair<int64_t, int64_t>>
1364 const int var = reduced_var.first;
1365 const int64_t lb = reduced_var.second.first;
1366 const int64_t ub = reduced_var.second.second;
1373 return neighborhood;
1379 return neighborhood;
1384 absl::BitGenRef random) {
1385 std::vector<int> removable_constraints;
1387 removable_constraints.reserve(num_constraints);
1388 for (
int c = 0; c < num_constraints; ++c) {
1395 removable_constraints.push_back(c);
1398 const int target_size =
1399 std::round((1.0 -
difficulty) * removable_constraints.size());
1401 const int random_start_index =
1402 absl::Uniform<int>(random, 0, removable_constraints.size());
1403 std::vector<int> removed_constraints;
1404 removed_constraints.reserve(target_size);
1405 int c = random_start_index;
1406 while (removed_constraints.size() < target_size) {
1407 removed_constraints.push_back(removable_constraints[c]);
1409 if (c == removable_constraints.size()) {
1421 std::vector<int> removable_constraints;
1423 constraint_weights_.reserve(num_constraints);
1425 for (
int c = 0; c < num_constraints; ++c) {
1432 constraint_weights_.push_back(3.0);
1433 num_removable_constraints_++;
1444 constraint_weights_.push_back(2.0);
1445 num_removable_constraints_++;
1454 constraint_weights_.push_back(1.0);
1455 num_removable_constraints_++;
1462 constraint_weights_.push_back(0.0);
1468void WeightedRandomRelaxationNeighborhoodGenerator::
1469 AdditionalProcessingOnSynchronize(
const SolveData& solve_data) {
1470 const IntegerValue best_objective_improvement =
1471 solve_data.new_objective_bound - solve_data.initial_best_objective_bound;
1473 const std::vector<int>& removed_constraints =
1474 removed_constraints_[solve_data.neighborhood_id];
1488 if (best_objective_improvement > 0) {
1490 for (
int c : removed_constraints) {
1491 if (constraint_weights_[c] <= 90.0) {
1492 constraint_weights_[c] += 10.0;
1494 constraint_weights_[c] = 100.0;
1498 best_objective_improvement < 0) {
1500 for (
int c : removed_constraints) {
1501 if (constraint_weights_[c] > 0.5) {
1502 constraint_weights_[c] -= 0.5;
1506 removed_constraints_.erase(solve_data.neighborhood_id);
1511 absl::BitGenRef random) {
1512 const int target_size =
1513 std::round((1.0 -
difficulty) * num_removable_constraints_);
1515 std::vector<int> removed_constraints;
1520 std::vector<std::pair<double, int>> constraint_removal_scores;
1521 std::uniform_real_distribution<double> random_var(0.0, 1.0);
1522 for (
int c = 0; c < constraint_weights_.size(); ++c) {
1523 if (constraint_weights_[c] <= 0)
continue;
1524 const double u = random_var(random);
1525 const double score = std::pow(u, (1 / constraint_weights_[c]));
1526 constraint_removal_scores.push_back({score, c});
1528 std::sort(constraint_removal_scores.rbegin(),
1529 constraint_removal_scores.rend());
1530 for (
int i = 0; i < target_size; ++i) {
1531 removed_constraints.push_back(constraint_removal_scores[i].second);
1537 result.
id = next_available_id_;
1538 next_available_id_++;
1539 removed_constraints_.insert({result.
id, removed_constraints});
#define CHECK_LT(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define CHECK_GT(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK(condition)
#define CHECK_LE(val1, val2)
#define VLOG(verboselevel)
bool AddEdge(int node1, int node2)
void SetNumberOfNodes(int num_nodes)
void Update(int num_decreases, int num_increases)
We call domain any subset of Int64 = [kint64min, kint64max].
bool IsIncludedIn(const Domain &domain) const
Returns true iff D is included in the given domain.
bool Contains(int64_t value) const
Returns true iff value is in Domain.
Domain UnionWith(const Domain &domain) const
Returns the union of D and domain.
bool IsFixed() const
Returns true iff the domain is reduced to a single value.
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
bool IsEmpty() const
Returns true if this is the empty set.
int64_t Max() const
Returns the max value of the domain.
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
const ::operations_research::sat::CircuitConstraintProto & circuit() const
const ::operations_research::sat::IntervalConstraintProto & interval() const
const ::operations_research::sat::NoOverlap2DConstraintProto & no_overlap_2d() const
ConstraintCase constraint_case() const
int32_t enforcement_literal(int index) const
const ::operations_research::sat::CumulativeConstraintProto & cumulative() const
const ::operations_research::sat::RoutesConstraintProto & routes() const
const ::operations_research::sat::NoOverlapConstraintProto & no_overlap() const
const ::operations_research::sat::CpObjectiveProto & objective() const
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
bool has_objective() const
::operations_research::sat::IntegerVariableProto * mutable_variables(int index)
int variables_size() const
::operations_research::sat::IntegerVariableProto * add_variables()
int constraints_size() const
const ::operations_research::sat::ConstraintProto & constraints(int index) const
int64_t domain(int index) const
int32_t vars(int index) const
int64_t coeffs(int index) const
int64_t solution(int index) const
int32_t intervals(int index) const
const std::string & name() const
void set_name(ArgT0 &&arg0, ArgT... args)
int64_t domain(int index) const
void add_domain(int64_t value)
const ::operations_research::sat::LinearExpressionProto & end() const
const ::operations_research::sat::LinearExpressionProto & start() const
const ::operations_research::sat::LinearExpressionProto & size() const
int32_t vars(int index) const
int64_t coeffs(int index) const
NeighborhoodGeneratorHelper(CpModelProto const *model_proto, SatParameters const *parameters, SharedResponseManager *shared_response, SharedTimeLimit *shared_time_limit=nullptr, SharedBoundsManager *shared_bounds=nullptr)
Neighborhood FullNeighborhood() const
Neighborhood FixAllVariables(const CpSolverResponse &initial_solution) const
const CpModelProto & ModelProto() const
const absl::Span< const int > TypeToConstraints(ConstraintProto::ConstraintCase type) const
Neighborhood FixGivenVariables(const CpSolverResponse &base_solution, const absl::flat_hash_set< int > &variables_to_fix) const
bool DifficultyMeansFullNeighborhood(double difficulty) const
Neighborhood NoNeighborhood() const
Neighborhood RelaxGivenVariables(const CpSolverResponse &initial_solution, const std::vector< int > &relaxed_variables) const
std::vector< int > GetActiveIntervals(const CpSolverResponse &initial_solution) const
const SharedResponseManager & shared_response() const
std::vector< int > ActiveVariables() const
const std::vector< std::vector< int > > & VarToConstraint() const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_)
bool IsActive(int var) const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_)
const std::vector< int > & ActiveVariablesWhileHoldingLock() const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_)
Neighborhood RemoveMarkedConstraints(const std::vector< int > &constraints_to_remove) const
void AddSolutionHinting(const CpSolverResponse &initial_solution, CpModelProto *model_proto) const
const std::vector< std::vector< int > > & ConstraintToVar() const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_)
void Synchronize() override
std::vector< std::vector< int > > GetRoutingPaths(const CpSolverResponse &initial_solution) const
virtual bool IsRelaxationGenerator() const
absl::Mutex generator_mutex_
virtual bool ReadyToGenerate() const
double difficulty() const
double GetUCBScore(int64_t total_num_calls) const
virtual void AdditionalProcessingOnSynchronize(const SolveData &solve_data)
const NeighborhoodGeneratorHelper & helper_
int32_t y_intervals(int index) const
int32_t x_intervals(int index) const
int32_t intervals(int index) const
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
bool ReadyToGenerate() const override
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
void GetChangedBounds(int id, std::vector< int > *variables, std::vector< int64_t > *new_lower_bounds, std::vector< int64_t > *new_upper_bounds)
bool HasNewSolution() const
const SharedSolutionRepository< int64_t > & SolutionsRepository() const
void LogMessage(std::string message)
IntegerValue GetInnerObjectiveLowerBound()
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
WeightedRandomRelaxationNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, const std::string &name)
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
CpModelProto const * model_proto
GurobiMPCallbackContext * context
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
std::vector< int > UsedVariables(const ConstraintProto &ct)
bool RefIsPositive(int ref)
std::vector< int > UsedIntervals(const ConstraintProto &ct)
bool DomainInProtoContains(const ProtoWithDomain &proto, int64_t value)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Neighborhood GenerateSchedulingNeighborhoodForRelaxation(const absl::Span< const int > intervals_to_relax, const CpSolverResponse &initial_solution, const NeighborhoodGeneratorHelper &helper)
RINSNeighborhood GetRINSNeighborhood(const SharedResponseManager *response_manager, const SharedRelaxationSolutionRepository *relaxation_solutions, const SharedLPSolutionRepository *lp_solutions, SharedIncompleteSolutionManager *incomplete_solutions, absl::BitGenRef random)
Collection of objects used to extend the Constraint Solver library.
int64_t CapSub(int64_t x, int64_t y)
int num_relaxed_variables
std::vector< int > variables_that_can_be_fixed_to_local_optimum
int num_relaxed_variables_in_objective
std::vector< int > constraints_to_ignore
std::vector< std::pair< int, int64_t > > fixed_vars
std::vector< std::pair< int, std::pair< int64_t, int64_t > > > reduced_domain_vars
#define VLOG_IS_ON(verboselevel)