40 shared_time_limit_(shared_time_limit),
41 shared_bounds_(shared_bounds),
42 shared_response_(shared_response) {
43 CHECK(shared_response_ !=
nullptr);
44 if (shared_bounds_ !=
nullptr) {
47 *model_proto_with_only_variables_.mutable_variables() =
48 model_proto_.variables();
49 RecomputeHelperData();
54 absl::MutexLock mutex_lock(&mutex_);
55 if (shared_bounds_ !=
nullptr) {
56 std::vector<int> model_variables;
57 std::vector<int64> new_lower_bounds;
58 std::vector<int64> new_upper_bounds;
60 &new_lower_bounds, &new_upper_bounds);
62 for (
int i = 0; i < model_variables.size(); ++i) {
63 const int var = model_variables[i];
64 const int64 new_lb = new_lower_bounds[i];
65 const int64 new_ub = new_upper_bounds[i];
68 model_proto_with_only_variables_.variables(
var).domain();
69 const int64 old_lb = domain.Get(0);
70 const int64 old_ub = domain.Get(domain.size() - 1);
71 VLOG(3) <<
"Variable: " <<
var <<
" old domain: [" << old_lb <<
", "
72 << old_ub <<
"] new domain: [" << new_lb <<
", " << new_ub
82 if (shared_time_limit_ !=
nullptr) shared_time_limit_->
Stop();
86 new_domain, model_proto_with_only_variables_.mutable_variables(
var));
90 if (!model_variables.empty()) {
91 RecomputeHelperData();
96 void NeighborhoodGeneratorHelper::RecomputeHelperData() {
103 var_to_constraint_.assign(model_proto_.variables_size(), {});
104 constraint_to_var_.assign(model_proto_.constraints_size(), {});
105 for (
int ct_index = 0; ct_index < model_proto_.constraints_size();
108 if (IsConstant(
var))
continue;
109 var_to_constraint_[
var].push_back(ct_index);
110 constraint_to_var_[ct_index].push_back(
var);
112 CHECK_LT(
var, model_proto_.variables_size());
116 type_to_constraints_.clear();
117 const int num_constraints = model_proto_.constraints_size();
118 for (
int c = 0; c < num_constraints; ++c) {
119 const int type = model_proto_.constraints(c).constraint_case();
120 if (type >= type_to_constraints_.size()) {
121 type_to_constraints_.resize(type + 1);
123 type_to_constraints_[type].push_back(c);
126 active_variables_.clear();
127 active_variables_set_.assign(model_proto_.variables_size(),
false);
129 if (parameters_.lns_focus_on_decision_variables()) {
130 for (
const auto& search_strategy : model_proto_.search_strategy()) {
131 for (
const int var : search_strategy.variables()) {
133 if (!active_variables_set_[pos_var] && !IsConstant(pos_var)) {
134 active_variables_set_[pos_var] =
true;
135 active_variables_.push_back(pos_var);
141 if (!active_variables_.empty())
return;
145 for (
int i = 0; i < model_proto_.variables_size(); ++i) {
146 if (!IsConstant(i)) {
147 active_variables_.push_back(i);
148 active_variables_set_[i] =
true;
154 return active_variables_set_[
var];
157 bool NeighborhoodGeneratorHelper::IsConstant(
int var)
const {
158 return model_proto_with_only_variables_.variables(
var).domain_size() == 2 &&
159 model_proto_with_only_variables_.variables(
var).domain(0) ==
160 model_proto_with_only_variables_.variables(
var).domain(1);
167 neighborhood.
cp_model = model_proto_;
168 *neighborhood.
cp_model.mutable_variables() =
169 model_proto_with_only_variables_.variables();
174 const CpSolverResponse& initial_solution,
175 const std::vector<int>& variables_to_fix)
const {
181 neighborhood.
cp_model.clear_solution_hint();
183 neighborhood.
cp_model.mutable_solution_hint()->add_vars(
var);
184 neighborhood.
cp_model.mutable_solution_hint()->add_values(
185 initial_solution.solution(
var));
188 neighborhood.
is_reduced = !variables_to_fix.empty();
189 if (!neighborhood.
is_reduced)
return neighborhood;
190 CHECK_EQ(initial_solution.solution_size(),
191 neighborhood.
cp_model.variables_size());
192 for (
const int var : variables_to_fix) {
193 neighborhood.
cp_model.mutable_variables(
var)->clear_domain();
194 neighborhood.
cp_model.mutable_variables(
var)->add_domain(
195 initial_solution.solution(
var));
196 neighborhood.
cp_model.mutable_variables(
var)->add_domain(
197 initial_solution.solution(
var));
208 const std::vector<int>& constraints_to_remove)
const {
213 if (constraints_to_remove.empty())
return neighborhood;
215 for (
const int constraint : constraints_to_remove) {
216 neighborhood.
cp_model.mutable_constraints(constraint)->Clear();
223 const CpSolverResponse& initial_solution,
224 const std::vector<int>& relaxed_variables)
const {
225 std::vector<bool> relaxed_variables_set(model_proto_.variables_size(),
false);
226 for (
const int var : relaxed_variables) relaxed_variables_set[
var] =
true;
227 std::vector<int> fixed_variables;
228 for (
const int i : active_variables_) {
229 if (!relaxed_variables_set[i]) {
230 fixed_variables.push_back(i);
237 const CpSolverResponse& initial_solution)
const {
238 std::vector<int> fixed_variables;
239 for (
const int i : active_variables_) {
240 fixed_variables.push_back(i);
250 absl::MutexLock mutex_lock(&
mutex_);
251 DCHECK_GE(total_num_calls, num_calls_);
252 if (num_calls_ <= 10)
return std::numeric_limits<double>::infinity();
253 return current_average_ + sqrt((2 * log(total_num_calls)) / num_calls_);
257 absl::MutexLock mutex_lock(&
mutex_);
261 std::sort(solve_data_.begin(), solve_data_.end());
264 int num_fully_solved_in_batch = 0;
265 int num_not_fully_solved_in_batch = 0;
267 for (
const SolveData& data : solve_data_) {
276 ++num_fully_solved_calls_;
277 ++num_fully_solved_in_batch;
279 ++num_not_fully_solved_in_batch;
288 const IntegerValue best_objective_improvement =
290 ? IntegerValue(
CapSub(data.new_objective_bound.value(),
291 data.initial_best_objective_bound.value()))
292 : IntegerValue(
CapSub(data.initial_best_objective.value(),
293 data.new_objective.value()));
294 if (best_objective_improvement > 0) {
295 num_consecutive_non_improving_calls_ = 0;
297 ++num_consecutive_non_improving_calls_;
302 const double gain_per_time_unit =
303 std::max(0.0,
static_cast<double>(best_objective_improvement.value())) /
304 (1.0 + data.deterministic_time);
305 if (num_calls_ <= 100) {
306 current_average_ += (gain_per_time_unit - current_average_) / num_calls_;
308 current_average_ = 0.9 * current_average_ + 0.1 * gain_per_time_unit;
311 deterministic_time_ += data.deterministic_time;
315 difficulty_.
Update(num_not_fully_solved_in_batch,
316 num_fully_solved_in_batch);
324 if (num_consecutive_non_improving_calls_ > 50) {
325 num_consecutive_non_improving_calls_ = 0;
326 deterministic_limit_ *= 1.02;
330 deterministic_limit_ =
std::min(60.0, deterministic_limit_);
338 template <
class Random>
339 void GetRandomSubset(
double relative_size, std::vector<int>* base,
343 std::shuffle(base->begin(), base->end(), *random);
344 const int target_size = std::round(relative_size * base->size());
345 base->resize(target_size);
351 const CpSolverResponse& initial_solution,
double difficulty,
354 GetRandomSubset(1.0 -
difficulty, &fixed_variables, random);
359 const CpSolverResponse& initial_solution,
double difficulty,
363 const int target_size = std::ceil(
difficulty * num_active_vars);
364 if (target_size == num_active_vars) {
367 CHECK_GT(target_size, 0) <<
difficulty <<
" " << num_active_vars;
369 std::vector<bool> visited_variables_set(num_model_vars,
false);
370 std::vector<int> relaxed_variables;
371 std::vector<int> visited_variables;
373 const int first_var =
376 visited_variables_set[first_var] =
true;
377 visited_variables.push_back(first_var);
378 relaxed_variables.push_back(first_var);
380 std::vector<int> random_variables;
381 for (
int i = 0; i < visited_variables.size(); ++i) {
382 random_variables.clear();
387 if (visited_variables_set[
var])
continue;
388 visited_variables_set[
var] =
true;
389 random_variables.push_back(
var);
393 std::shuffle(random_variables.begin(), random_variables.end(), *random);
394 for (
const int var : random_variables) {
395 if (relaxed_variables.size() < target_size) {
396 visited_variables.push_back(
var);
398 relaxed_variables.push_back(
var);
404 if (relaxed_variables.size() >= target_size)
break;
411 const CpSolverResponse& initial_solution,
double difficulty,
415 const int target_size = std::ceil(
difficulty * num_active_vars);
417 if (num_constraints == 0 || target_size == num_active_vars) {
420 CHECK_GT(target_size, 0);
422 std::vector<bool> visited_variables_set(num_model_vars,
false);
423 std::vector<int> relaxed_variables;
424 std::vector<bool> added_constraints(num_constraints,
false);
425 std::vector<int> next_constraints;
428 next_constraints.push_back(absl::Uniform<int>(*random, 0, num_constraints));
429 added_constraints[next_constraints.back()] =
true;
431 std::vector<int> random_variables;
432 while (relaxed_variables.size() < target_size) {
434 if (next_constraints.empty())
break;
437 const int i = absl::Uniform<int>(*random, 0, next_constraints.size());
438 const int contraint_index = next_constraints[i];
439 std::swap(next_constraints[i], next_constraints.back());
440 next_constraints.pop_back();
444 CHECK_LT(contraint_index, num_constraints);
446 std::shuffle(random_variables.begin(), random_variables.end(), *random);
447 for (
const int var : random_variables) {
448 if (visited_variables_set[
var])
continue;
449 visited_variables_set[
var] =
true;
451 relaxed_variables.push_back(
var);
453 if (relaxed_variables.size() == target_size)
break;
456 if (added_constraints[
ct])
continue;
457 added_constraints[
ct] =
true;
458 next_constraints.push_back(
ct);
466 const absl::Span<const int> intervals_to_relax,
467 const CpSolverResponse& initial_solution,
471 (intervals_to_relax.size() <
475 std::set<int> ignored_intervals(intervals_to_relax.begin(),
476 intervals_to_relax.end());
480 if (ignored_intervals.count(i))
continue;
482 const ConstraintProto& interval_ct = neighborhood.
cp_model.constraints(i);
483 if (interval_ct.enforcement_literal().empty())
continue;
485 CHECK_EQ(interval_ct.enforcement_literal().size(), 1);
486 const int enforcement_ref = interval_ct.enforcement_literal(0);
487 const int enforcement_var =
PositiveRef(enforcement_ref);
488 const int value = initial_solution.solution(enforcement_var);
491 neighborhood.
cp_model.mutable_variables(enforcement_var)->clear_domain();
492 neighborhood.
cp_model.mutable_variables(enforcement_var)->add_domain(
value);
493 neighborhood.
cp_model.mutable_variables(enforcement_var)->add_domain(
value);
498 ignored_intervals.insert(i);
504 std::vector<std::pair<int64, int>> start_interval_pairs;
506 neighborhood.
cp_model.constraints(c).no_overlap().intervals()) {
507 if (ignored_intervals.count(i))
continue;
508 const ConstraintProto& interval_ct = neighborhood.
cp_model.constraints(i);
511 const int size_var = interval_ct.interval().size();
512 if (initial_solution.solution(size_var) == 0)
continue;
514 const int start_var = interval_ct.interval().start();
515 const int64 start_value = initial_solution.solution(start_var);
516 start_interval_pairs.push_back({start_value, i});
518 std::sort(start_interval_pairs.begin(), start_interval_pairs.end());
521 for (
int i = 0; i + 1 < start_interval_pairs.size(); ++i) {
522 const int before_var =
523 neighborhood.
cp_model.constraints(start_interval_pairs[i].second)
526 const int after_var =
527 neighborhood.
cp_model.constraints(start_interval_pairs[i + 1].second)
530 CHECK_LE(initial_solution.solution(before_var),
531 initial_solution.solution(after_var));
533 LinearConstraintProto* linear =
534 neighborhood.
cp_model.add_constraints()->mutable_linear();
536 linear->add_domain(0);
537 linear->add_vars(before_var);
538 linear->add_coeffs(1);
539 linear->add_vars(after_var);
540 linear->add_coeffs(-1);
547 neighborhood.
cp_model.clear_solution_hint();
549 neighborhood.
cp_model.mutable_solution_hint()->add_vars(
var);
550 neighborhood.
cp_model.mutable_solution_hint()->add_values(
551 initial_solution.solution(
var));
559 const CpSolverResponse& initial_solution,
double difficulty,
562 std::vector<int> intervals_to_relax(span.begin(), span.end());
563 GetRandomSubset(
difficulty, &intervals_to_relax, random);
570 const CpSolverResponse& initial_solution,
double difficulty,
572 std::vector<std::pair<int64, int>> start_interval_pairs;
576 const int start_var = interval_ct.interval().start();
577 const int64 start_value = initial_solution.solution(start_var);
578 start_interval_pairs.push_back({start_value, i});
580 std::sort(start_interval_pairs.begin(), start_interval_pairs.end());
581 const int relaxed_size = std::floor(
difficulty * start_interval_pairs.size());
583 std::uniform_int_distribution<int> random_var(
584 0, start_interval_pairs.size() - relaxed_size - 1);
585 const int random_start_index = random_var(*random);
586 std::vector<int> intervals_to_relax;
589 for (
int i = random_start_index; i < relaxed_size; ++i) {
590 intervals_to_relax.push_back(start_interval_pairs[i].second);
597 if (incomplete_solutions_ !=
nullptr) {
601 if (response_manager_ !=
nullptr) {
609 if (lp_solutions_ !=
nullptr && lp_solutions_->
NumSolutions() > 0) {
613 if (relaxation_solutions_ !=
nullptr &&
621 const CpSolverResponse& initial_solution,
double difficulty,
626 const bool lp_solution_available =
627 (lp_solutions_ !=
nullptr && lp_solutions_->
NumSolutions() > 0);
629 const bool relaxation_solution_available =
630 (relaxation_solutions_ !=
nullptr &&
633 const bool incomplete_solution_available =
634 (incomplete_solutions_ !=
nullptr &&
637 if (!lp_solution_available && !relaxation_solution_available &&
638 !incomplete_solution_available) {
646 std::bernoulli_distribution random_bool(0.5);
647 const bool use_lp_relaxation =
648 (lp_solution_available && relaxation_solution_available)
649 ? random_bool(*random)
650 : lp_solution_available;
651 if (use_lp_relaxation) {
654 nullptr, lp_solutions_,
655 incomplete_solutions_, random);
657 incomplete_solution_available ?
"incomplete" :
"lp";
659 CHECK(relaxation_solution_available || incomplete_solution_available);
661 response_manager_, relaxation_solutions_,
662 nullptr, incomplete_solutions_, random);
664 incomplete_solution_available ?
"incomplete" :
"relaxation";
673 for (
const std::pair</*model_var*/ int, /*value*/ int64> fixed_var :
675 const int var = fixed_var.first;
677 if (
var >= neighborhood.
cp_model.variables_size())
continue;
687 neighborhood.
cp_model.mutable_variables(
var)->clear_domain();
693 for (
const std::pair<
int, std::pair<int64, int64>>
695 const int var = reduced_var.first;
696 const int64 lb = reduced_var.second.first;
697 const int64 ub = reduced_var.second.second;
698 if (
var >= neighborhood.
cp_model.variables_size())
continue;
714 const CpSolverResponse& initial_solution,
double difficulty,
716 std::vector<int> removable_constraints;
718 removable_constraints.reserve(num_constraints);
719 for (
int c = 0; c < num_constraints; ++c) {
723 ConstraintProto::kInterval) {
726 removable_constraints.push_back(c);
729 const int target_size =
730 std::round((1.0 -
difficulty) * removable_constraints.size());
732 const int random_start_index =
733 absl::Uniform<int>(*random, 0, removable_constraints.size());
734 std::vector<int> removed_constraints;
735 removed_constraints.reserve(target_size);
736 int c = random_start_index;
737 while (removed_constraints.size() < target_size) {
738 removed_constraints.push_back(removable_constraints[c]);
740 if (c == removable_constraints.size()) {
752 std::vector<int> removable_constraints;
754 constraint_weights_.reserve(num_constraints);
756 for (
int c = 0; c < num_constraints; ++c) {
758 case ConstraintProto::kCumulative:
759 case ConstraintProto::kAllDiff:
760 case ConstraintProto::kElement:
761 case ConstraintProto::kRoutes:
762 case ConstraintProto::kCircuit:
763 case ConstraintProto::kCircuitCovering:
764 constraint_weights_.push_back(3.0);
765 num_removable_constraints_++;
767 case ConstraintProto::kBoolOr:
768 case ConstraintProto::kBoolAnd:
769 case ConstraintProto::kBoolXor:
770 case ConstraintProto::kIntProd:
771 case ConstraintProto::kIntDiv:
772 case ConstraintProto::kIntMod:
773 case ConstraintProto::kIntMax:
774 case ConstraintProto::kLinMax:
775 case ConstraintProto::kIntMin:
776 case ConstraintProto::kLinMin:
777 case ConstraintProto::kNoOverlap:
778 case ConstraintProto::kNoOverlap2D:
779 constraint_weights_.push_back(2.0);
780 num_removable_constraints_++;
782 case ConstraintProto::kLinear:
783 case ConstraintProto::kTable:
784 case ConstraintProto::kAutomaton:
785 case ConstraintProto::kInverse:
786 case ConstraintProto::kReservoir:
787 case ConstraintProto::kAtMostOne:
788 constraint_weights_.push_back(1.0);
789 num_removable_constraints_++;
791 case ConstraintProto::CONSTRAINT_NOT_SET:
792 case ConstraintProto::kInterval:
795 constraint_weights_.push_back(0.0);
801 void WeightedRandomRelaxationNeighborhoodGenerator::
802 AdditionalProcessingOnSynchronize(
const SolveData& solve_data) {
803 const IntegerValue best_objective_improvement =
804 solve_data.new_objective_bound - solve_data.initial_best_objective_bound;
806 const std::vector<int>& removed_constraints =
807 removed_constraints_[solve_data.neighborhood_id];
821 if (best_objective_improvement > 0) {
823 for (
int c : removed_constraints) {
824 if (constraint_weights_[c] <= 90.0) {
825 constraint_weights_[c] += 10.0;
827 constraint_weights_[c] = 100.0;
831 best_objective_improvement < 0) {
833 for (
int c : removed_constraints) {
834 if (constraint_weights_[c] > 0.5) {
835 constraint_weights_[c] -= 0.5;
839 removed_constraints_.erase(solve_data.neighborhood_id);
843 const CpSolverResponse& initial_solution,
double difficulty,
845 const int target_size =
846 std::round((1.0 -
difficulty) * num_removable_constraints_);
848 std::vector<int> removed_constraints;
853 std::vector<std::pair<double, int>> constraint_removal_scores;
854 std::uniform_real_distribution<double> random_var(0.0, 1.0);
855 for (
int c = 0; c < constraint_weights_.size(); ++c) {
856 if (constraint_weights_[c] <= 0)
continue;
857 const double u = random_var(*random);
858 const double score = std::pow(u, (1 / constraint_weights_[c]));
859 constraint_removal_scores.push_back({score, c});
861 std::sort(constraint_removal_scores.rbegin(),
862 constraint_removal_scores.rend());
863 for (
int i = 0; i < target_size; ++i) {
864 removed_constraints.push_back(constraint_removal_scores[i].second);
868 absl::MutexLock mutex_lock(&
mutex_);
869 result.
id = next_available_id_;
870 next_available_id_++;
871 removed_constraints_.insert({result.
id, removed_constraints});