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
96 new_domain, model_proto_with_only_variables_.mutable_variables(
var));
100 if (!model_variables.empty()) {
101 RecomputeHelperData();
106 void NeighborhoodGeneratorHelper::RecomputeHelperData() {
113 var_to_constraint_.assign(model_proto_.variables_size(), {});
114 constraint_to_var_.assign(model_proto_.constraints_size(), {});
115 for (
int ct_index = 0; ct_index < model_proto_.constraints_size();
118 if (IsConstant(
var))
continue;
119 var_to_constraint_[
var].push_back(ct_index);
120 constraint_to_var_[ct_index].push_back(
var);
122 CHECK_LT(
var, model_proto_.variables_size());
126 type_to_constraints_.clear();
127 const int num_constraints = model_proto_.constraints_size();
128 for (
int c = 0; c < num_constraints; ++c) {
129 const int type = model_proto_.constraints(c).constraint_case();
130 if (type >= type_to_constraints_.size()) {
131 type_to_constraints_.resize(type + 1);
133 type_to_constraints_[type].push_back(c);
136 active_variables_.clear();
137 active_variables_set_.assign(model_proto_.variables_size(),
false);
139 if (parameters_.lns_focus_on_decision_variables()) {
140 for (
const auto& search_strategy : model_proto_.search_strategy()) {
141 for (
const int var : search_strategy.variables()) {
143 if (!active_variables_set_[pos_var] && !IsConstant(pos_var)) {
144 active_variables_set_[pos_var] =
true;
145 active_variables_.push_back(pos_var);
151 if (!active_variables_.empty())
return;
155 for (
int i = 0; i < model_proto_.variables_size(); ++i) {
156 if (!IsConstant(i)) {
157 active_variables_.push_back(i);
158 active_variables_set_[i] =
true;
164 return active_variables_set_[
var];
167 bool NeighborhoodGeneratorHelper::IsConstant(
int var)
const {
168 return model_proto_with_only_variables_.variables(
var).domain_size() == 2 &&
169 model_proto_with_only_variables_.variables(
var).domain(0) ==
170 model_proto_with_only_variables_.variables(
var).domain(1);
177 neighborhood.
cp_model = model_proto_;
178 *neighborhood.
cp_model.mutable_variables() =
179 model_proto_with_only_variables_.variables();
184 const CpSolverResponse& initial_solution,
185 const std::vector<int>& variables_to_fix)
const {
191 neighborhood.
cp_model.clear_solution_hint();
193 neighborhood.
cp_model.mutable_solution_hint()->add_vars(
var);
194 neighborhood.
cp_model.mutable_solution_hint()->add_values(
195 initial_solution.solution(
var));
198 neighborhood.
is_reduced = !variables_to_fix.empty();
199 if (!neighborhood.
is_reduced)
return neighborhood;
200 CHECK_EQ(initial_solution.solution_size(),
201 neighborhood.
cp_model.variables_size());
202 for (
const int var : variables_to_fix) {
203 neighborhood.
cp_model.mutable_variables(
var)->clear_domain();
204 neighborhood.
cp_model.mutable_variables(
var)->add_domain(
205 initial_solution.solution(
var));
206 neighborhood.
cp_model.mutable_variables(
var)->add_domain(
207 initial_solution.solution(
var));
218 const std::vector<int>& constraints_to_remove)
const {
223 if (constraints_to_remove.empty())
return neighborhood;
225 for (
const int constraint : constraints_to_remove) {
226 neighborhood.
cp_model.mutable_constraints(constraint)->Clear();
233 const CpSolverResponse& initial_solution,
234 const std::vector<int>& relaxed_variables)
const {
235 std::vector<bool> relaxed_variables_set(model_proto_.variables_size(),
false);
236 for (
const int var : relaxed_variables) relaxed_variables_set[
var] =
true;
237 std::vector<int> fixed_variables;
238 for (
const int i : active_variables_) {
239 if (!relaxed_variables_set[i]) {
240 fixed_variables.push_back(i);
247 const CpSolverResponse& initial_solution)
const {
248 std::vector<int> fixed_variables;
249 for (
const int i : active_variables_) {
250 fixed_variables.push_back(i);
260 absl::MutexLock mutex_lock(&
mutex_);
261 DCHECK_GE(total_num_calls, num_calls_);
262 if (num_calls_ <= 10)
return std::numeric_limits<double>::infinity();
263 return current_average_ + sqrt((2 * log(total_num_calls)) / num_calls_);
267 absl::MutexLock mutex_lock(&
mutex_);
271 std::sort(solve_data_.begin(), solve_data_.end());
274 int num_fully_solved_in_batch = 0;
275 int num_not_fully_solved_in_batch = 0;
277 for (
const SolveData& data : solve_data_) {
286 ++num_fully_solved_calls_;
287 ++num_fully_solved_in_batch;
289 ++num_not_fully_solved_in_batch;
298 const IntegerValue best_objective_improvement =
300 ? IntegerValue(
CapSub(data.new_objective_bound.value(),
301 data.initial_best_objective_bound.value()))
302 : IntegerValue(
CapSub(data.initial_best_objective.value(),
303 data.new_objective.value()));
304 if (best_objective_improvement > 0) {
305 num_consecutive_non_improving_calls_ = 0;
307 ++num_consecutive_non_improving_calls_;
312 const double gain_per_time_unit =
313 std::max(0.0,
static_cast<double>(best_objective_improvement.value())) /
314 (1.0 + data.deterministic_time);
315 if (num_calls_ <= 100) {
316 current_average_ += (gain_per_time_unit - current_average_) / num_calls_;
318 current_average_ = 0.9 * current_average_ + 0.1 * gain_per_time_unit;
321 deterministic_time_ += data.deterministic_time;
325 difficulty_.
Update(num_not_fully_solved_in_batch,
326 num_fully_solved_in_batch);
334 if (num_consecutive_non_improving_calls_ > 50) {
335 num_consecutive_non_improving_calls_ = 0;
336 deterministic_limit_ *= 1.02;
340 deterministic_limit_ =
std::min(60.0, deterministic_limit_);
348 template <
class Random>
349 void GetRandomSubset(
double relative_size, std::vector<int>* base,
353 std::shuffle(base->begin(), base->end(), *random);
354 const int target_size = std::round(relative_size * base->size());
355 base->resize(target_size);
361 const CpSolverResponse& initial_solution,
double difficulty,
364 GetRandomSubset(1.0 -
difficulty, &fixed_variables, random);
369 const CpSolverResponse& initial_solution,
double difficulty,
373 const int target_size = std::ceil(
difficulty * num_active_vars);
374 if (target_size == num_active_vars) {
377 CHECK_GT(target_size, 0) <<
difficulty <<
" " << num_active_vars;
379 std::vector<bool> visited_variables_set(num_model_vars,
false);
380 std::vector<int> relaxed_variables;
381 std::vector<int> visited_variables;
383 const int first_var =
386 visited_variables_set[first_var] =
true;
387 visited_variables.push_back(first_var);
388 relaxed_variables.push_back(first_var);
390 std::vector<int> random_variables;
391 for (
int i = 0; i < visited_variables.size(); ++i) {
392 random_variables.clear();
397 if (visited_variables_set[
var])
continue;
398 visited_variables_set[
var] =
true;
399 random_variables.push_back(
var);
403 std::shuffle(random_variables.begin(), random_variables.end(), *random);
404 for (
const int var : random_variables) {
405 if (relaxed_variables.size() < target_size) {
406 visited_variables.push_back(
var);
408 relaxed_variables.push_back(
var);
414 if (relaxed_variables.size() >= target_size)
break;
421 const CpSolverResponse& initial_solution,
double difficulty,
425 const int target_size = std::ceil(
difficulty * num_active_vars);
427 if (num_constraints == 0 || target_size == num_active_vars) {
430 CHECK_GT(target_size, 0);
432 std::vector<bool> visited_variables_set(num_model_vars,
false);
433 std::vector<int> relaxed_variables;
434 std::vector<bool> added_constraints(num_constraints,
false);
435 std::vector<int> next_constraints;
438 next_constraints.push_back(absl::Uniform<int>(*random, 0, num_constraints));
439 added_constraints[next_constraints.back()] =
true;
441 std::vector<int> random_variables;
442 while (relaxed_variables.size() < target_size) {
444 if (next_constraints.empty())
break;
447 const int i = absl::Uniform<int>(*random, 0, next_constraints.size());
448 const int contraint_index = next_constraints[i];
449 std::swap(next_constraints[i], next_constraints.back());
450 next_constraints.pop_back();
454 CHECK_LT(contraint_index, num_constraints);
456 std::shuffle(random_variables.begin(), random_variables.end(), *random);
457 for (
const int var : random_variables) {
458 if (visited_variables_set[
var])
continue;
459 visited_variables_set[
var] =
true;
461 relaxed_variables.push_back(
var);
463 if (relaxed_variables.size() == target_size)
break;
466 if (added_constraints[
ct])
continue;
467 added_constraints[
ct] =
true;
468 next_constraints.push_back(
ct);
476 const absl::Span<const int> intervals_to_relax,
477 const CpSolverResponse& initial_solution,
481 (intervals_to_relax.size() <
485 std::set<int> ignored_intervals(intervals_to_relax.begin(),
486 intervals_to_relax.end());
490 if (ignored_intervals.count(i))
continue;
492 const ConstraintProto& interval_ct = neighborhood.
cp_model.constraints(i);
493 if (interval_ct.enforcement_literal().empty())
continue;
495 CHECK_EQ(interval_ct.enforcement_literal().size(), 1);
496 const int enforcement_ref = interval_ct.enforcement_literal(0);
497 const int enforcement_var =
PositiveRef(enforcement_ref);
498 const int value = initial_solution.solution(enforcement_var);
501 neighborhood.
cp_model.mutable_variables(enforcement_var)->clear_domain();
502 neighborhood.
cp_model.mutable_variables(enforcement_var)->add_domain(
value);
503 neighborhood.
cp_model.mutable_variables(enforcement_var)->add_domain(
value);
508 ignored_intervals.insert(i);
514 std::vector<std::pair<int64, int>> start_interval_pairs;
516 neighborhood.
cp_model.constraints(c).no_overlap().intervals()) {
517 if (ignored_intervals.count(i))
continue;
518 const ConstraintProto& interval_ct = neighborhood.
cp_model.constraints(i);
521 const int size_var = interval_ct.interval().size();
522 if (initial_solution.solution(size_var) == 0)
continue;
524 const int start_var = interval_ct.interval().start();
525 const int64 start_value = initial_solution.solution(start_var);
526 start_interval_pairs.push_back({start_value, i});
528 std::sort(start_interval_pairs.begin(), start_interval_pairs.end());
531 for (
int i = 0; i + 1 < start_interval_pairs.size(); ++i) {
532 const int before_var =
533 neighborhood.
cp_model.constraints(start_interval_pairs[i].second)
536 const int after_var =
537 neighborhood.
cp_model.constraints(start_interval_pairs[i + 1].second)
540 CHECK_LE(initial_solution.solution(before_var),
541 initial_solution.solution(after_var));
543 LinearConstraintProto* linear =
544 neighborhood.
cp_model.add_constraints()->mutable_linear();
546 linear->add_domain(0);
547 linear->add_vars(before_var);
548 linear->add_coeffs(1);
549 linear->add_vars(after_var);
550 linear->add_coeffs(-1);
557 neighborhood.
cp_model.clear_solution_hint();
559 neighborhood.
cp_model.mutable_solution_hint()->add_vars(
var);
560 neighborhood.
cp_model.mutable_solution_hint()->add_values(
561 initial_solution.solution(
var));
569 const CpSolverResponse& initial_solution,
double difficulty,
572 std::vector<int> intervals_to_relax(span.begin(), span.end());
573 GetRandomSubset(
difficulty, &intervals_to_relax, random);
580 const CpSolverResponse& initial_solution,
double difficulty,
582 std::vector<std::pair<int64, int>> start_interval_pairs;
586 const int start_var = interval_ct.interval().start();
587 const int64 start_value = initial_solution.solution(start_var);
588 start_interval_pairs.push_back({start_value, i});
590 std::sort(start_interval_pairs.begin(), start_interval_pairs.end());
591 const int relaxed_size = std::floor(
difficulty * start_interval_pairs.size());
593 std::uniform_int_distribution<int> random_var(
594 0, start_interval_pairs.size() - relaxed_size - 1);
595 const int random_start_index = random_var(*random);
596 std::vector<int> intervals_to_relax;
599 for (
int i = random_start_index; i < relaxed_size; ++i) {
600 intervals_to_relax.push_back(start_interval_pairs[i].second);
607 if (incomplete_solutions_ !=
nullptr) {
611 if (response_manager_ !=
nullptr) {
619 if (lp_solutions_ !=
nullptr && lp_solutions_->
NumSolutions() > 0) {
623 if (relaxation_solutions_ !=
nullptr &&
631 const CpSolverResponse& initial_solution,
double difficulty,
636 const bool lp_solution_available =
637 (lp_solutions_ !=
nullptr && lp_solutions_->
NumSolutions() > 0);
639 const bool relaxation_solution_available =
640 (relaxation_solutions_ !=
nullptr &&
643 const bool incomplete_solution_available =
644 (incomplete_solutions_ !=
nullptr &&
647 if (!lp_solution_available && !relaxation_solution_available &&
648 !incomplete_solution_available) {
656 std::bernoulli_distribution random_bool(0.5);
657 const bool use_lp_relaxation =
658 (lp_solution_available && relaxation_solution_available)
659 ? random_bool(*random)
660 : lp_solution_available;
661 if (use_lp_relaxation) {
664 nullptr, lp_solutions_,
665 incomplete_solutions_, random);
667 incomplete_solution_available ?
"incomplete" :
"lp";
669 CHECK(relaxation_solution_available || incomplete_solution_available);
671 response_manager_, relaxation_solutions_,
672 nullptr, incomplete_solutions_, random);
674 incomplete_solution_available ?
"incomplete" :
"relaxation";
683 for (
const std::pair</*model_var*/ int, /*value*/ int64> fixed_var :
685 const int var = fixed_var.first;
687 if (
var >= neighborhood.
cp_model.variables_size())
continue;
697 neighborhood.
cp_model.mutable_variables(
var)->clear_domain();
703 for (
const std::pair<
int, std::pair<int64, int64>>
705 const int var = reduced_var.first;
706 const int64 lb = reduced_var.second.first;
707 const int64 ub = reduced_var.second.second;
708 if (
var >= neighborhood.
cp_model.variables_size())
continue;
724 const CpSolverResponse& initial_solution,
double difficulty,
726 std::vector<int> removable_constraints;
728 removable_constraints.reserve(num_constraints);
729 for (
int c = 0; c < num_constraints; ++c) {
733 ConstraintProto::kInterval) {
736 removable_constraints.push_back(c);
739 const int target_size =
740 std::round((1.0 -
difficulty) * removable_constraints.size());
742 const int random_start_index =
743 absl::Uniform<int>(*random, 0, removable_constraints.size());
744 std::vector<int> removed_constraints;
745 removed_constraints.reserve(target_size);
746 int c = random_start_index;
747 while (removed_constraints.size() < target_size) {
748 removed_constraints.push_back(removable_constraints[c]);
750 if (c == removable_constraints.size()) {
762 std::vector<int> removable_constraints;
764 constraint_weights_.reserve(num_constraints);
766 for (
int c = 0; c < num_constraints; ++c) {
768 case ConstraintProto::kCumulative:
769 case ConstraintProto::kAllDiff:
770 case ConstraintProto::kElement:
771 case ConstraintProto::kRoutes:
772 case ConstraintProto::kCircuit:
773 case ConstraintProto::kCircuitCovering:
774 constraint_weights_.push_back(3.0);
775 num_removable_constraints_++;
777 case ConstraintProto::kBoolOr:
778 case ConstraintProto::kBoolAnd:
779 case ConstraintProto::kBoolXor:
780 case ConstraintProto::kIntProd:
781 case ConstraintProto::kIntDiv:
782 case ConstraintProto::kIntMod:
783 case ConstraintProto::kIntMax:
784 case ConstraintProto::kLinMax:
785 case ConstraintProto::kIntMin:
786 case ConstraintProto::kLinMin:
787 case ConstraintProto::kNoOverlap:
788 case ConstraintProto::kNoOverlap2D:
789 constraint_weights_.push_back(2.0);
790 num_removable_constraints_++;
792 case ConstraintProto::kLinear:
793 case ConstraintProto::kTable:
794 case ConstraintProto::kAutomaton:
795 case ConstraintProto::kInverse:
796 case ConstraintProto::kReservoir:
797 case ConstraintProto::kAtMostOne:
798 constraint_weights_.push_back(1.0);
799 num_removable_constraints_++;
801 case ConstraintProto::CONSTRAINT_NOT_SET:
802 case ConstraintProto::kInterval:
805 constraint_weights_.push_back(0.0);
811 void WeightedRandomRelaxationNeighborhoodGenerator::
812 AdditionalProcessingOnSynchronize(
const SolveData& solve_data) {
813 const IntegerValue best_objective_improvement =
814 solve_data.new_objective_bound - solve_data.initial_best_objective_bound;
816 const std::vector<int>& removed_constraints =
817 removed_constraints_[solve_data.neighborhood_id];
831 if (best_objective_improvement > 0) {
833 for (
int c : removed_constraints) {
834 if (constraint_weights_[c] <= 90.0) {
835 constraint_weights_[c] += 10.0;
837 constraint_weights_[c] = 100.0;
841 best_objective_improvement < 0) {
843 for (
int c : removed_constraints) {
844 if (constraint_weights_[c] > 0.5) {
845 constraint_weights_[c] -= 0.5;
849 removed_constraints_.erase(solve_data.neighborhood_id);
853 const CpSolverResponse& initial_solution,
double difficulty,
855 const int target_size =
856 std::round((1.0 -
difficulty) * num_removable_constraints_);
858 std::vector<int> removed_constraints;
863 std::vector<std::pair<double, int>> constraint_removal_scores;
864 std::uniform_real_distribution<double> random_var(0.0, 1.0);
865 for (
int c = 0; c < constraint_weights_.size(); ++c) {
866 if (constraint_weights_[c] <= 0)
continue;
867 const double u = random_var(*random);
868 const double score = std::pow(u, (1 / constraint_weights_[c]));
869 constraint_removal_scores.push_back({score, c});
871 std::sort(constraint_removal_scores.rbegin(),
872 constraint_removal_scores.rend());
873 for (
int i = 0; i < target_size; ++i) {
874 removed_constraints.push_back(constraint_removal_scores[i].second);
878 absl::MutexLock mutex_lock(&
mutex_);
879 result.
id = next_available_id_;
880 next_available_id_++;
881 removed_constraints_.insert({result.
id, removed_constraints});