25#include "absl/container/flat_hash_map.h"
26#include "absl/container/flat_hash_set.h"
27#include "absl/meta/type_traits.h"
28#include "absl/random/bit_gen_ref.h"
29#include "absl/random/distributions.h"
30#include "absl/strings/str_cat.h"
31#include "absl/strings/str_join.h"
32#include "absl/synchronization/mutex.h"
33#include "absl/time/clock.h"
34#include "absl/time/time.h"
35#include "absl/types/span.h"
38#include "ortools/sat/cp_model.pb.h"
45#include "ortools/sat/sat_parameters.pb.h"
64 shared_time_limit_(shared_time_limit),
65 shared_bounds_(shared_bounds),
66 shared_response_(shared_response) {
67 CHECK(shared_response_ !=
nullptr);
68 if (shared_bounds_ !=
nullptr) {
71 *model_proto_with_only_variables_.mutable_variables() =
72 model_proto_.variables();
73 InitializeHelperData();
74 RecomputeHelperData();
76 last_logging_time_ = absl::Now();
80 if (shared_bounds_ !=
nullptr) {
81 std::vector<int> model_variables;
82 std::vector<int64_t> new_lower_bounds;
83 std::vector<int64_t> new_upper_bounds;
85 &new_lower_bounds, &new_upper_bounds);
87 bool new_variables_have_been_fixed =
false;
90 absl::MutexLock domain_lock(&domain_mutex_);
92 for (
int i = 0; i < model_variables.size(); ++i) {
93 const int var = model_variables[i];
94 const int64_t new_lb = new_lower_bounds[i];
95 const int64_t new_ub = new_upper_bounds[i];
98 model_proto_with_only_variables_.variables(
var).domain();
99 const int64_t old_lb = domain.Get(0);
100 const int64_t old_ub = domain.Get(domain.size() - 1);
101 VLOG(3) <<
"Variable: " <<
var <<
" old domain: [" << old_lb <<
", "
102 << old_ub <<
"] new domain: [" << new_lb <<
", " << new_ub
106 model_proto_with_only_variables_.variables(
var));
127 model_proto_with_only_variables_.mutable_variables(
var));
128 new_variables_have_been_fixed |= new_domain.
IsFixed();
133 if (new_variables_have_been_fixed) {
134 RecomputeHelperData();
139bool NeighborhoodGeneratorHelper::ObjectiveDomainIsConstraining()
const {
140 if (!model_proto_.has_objective())
return false;
141 if (model_proto_.objective().domain().empty())
return false;
143 int64_t min_activity = 0;
144 int64_t max_activity = 0;
145 const int num_terms = model_proto_.objective().vars().size();
146 for (
int i = 0; i < num_terms; ++i) {
148 const int64_t
coeff = model_proto_.objective().coeffs(i);
149 const auto& var_domain =
150 model_proto_with_only_variables_.variables(
var).domain();
151 const int64_t v1 =
coeff * var_domain[0];
152 const int64_t v2 =
coeff * var_domain[var_domain.size() - 1];
158 const Domain inferred_domain =
159 Domain(min_activity, max_activity)
165void NeighborhoodGeneratorHelper::InitializeHelperData() {
166 type_to_constraints_.clear();
167 const int num_constraints = model_proto_.constraints_size();
168 for (
int c = 0; c < num_constraints; ++c) {
169 const int type = model_proto_.constraints(c).constraint_case();
170 if (type >= type_to_constraints_.size()) {
171 type_to_constraints_.resize(type + 1);
173 type_to_constraints_[type].push_back(c);
176 const int num_variables = model_proto_.variables().size();
177 is_in_objective_.resize(num_variables,
false);
178 if (model_proto_.has_objective()) {
179 for (
const int ref : model_proto_.objective().vars()) {
187void NeighborhoodGeneratorHelper::RecomputeHelperData() {
189 absl::ReaderMutexLock domain_lock(&domain_mutex_);
203 CpModelProto mapping_proto;
204 simplied_model_proto_.Clear();
205 *simplied_model_proto_.mutable_variables() =
206 model_proto_with_only_variables_.variables();
207 PresolveContext
context(&local_model, &simplied_model_proto_,
213 copier.ImportAndSimplifyConstraints(model_proto_, {});
219 const auto& constraints = simplied_model_proto_.constraints();
220 var_to_constraint_.assign(model_proto_.variables_size(), {});
221 constraint_to_var_.assign(constraints.size(), {});
222 int reduced_ct_index = 0;
223 for (
int ct_index = 0; ct_index < constraints.size(); ++ct_index) {
229 if (constraints[ct_index].constraint_case() == ConstraintProto::kInterval) {
234 if (IsConstant(
var))
continue;
235 constraint_to_var_[reduced_ct_index].push_back(
var);
242 if (IsConstant(
var))
continue;
243 constraint_to_var_[reduced_ct_index].push_back(
var);
249 if (constraint_to_var_[reduced_ct_index].size() <= 1) {
250 constraint_to_var_[reduced_ct_index].clear();
255 for (
const int var : constraint_to_var_[reduced_ct_index]) {
256 var_to_constraint_[
var].push_back(reduced_ct_index);
260 constraint_to_var_.resize(reduced_ct_index);
264 active_variables_.clear();
265 const int num_variables = model_proto_.variables_size();
266 active_variables_set_.assign(num_variables,
false);
267 for (
int i = 0; i < num_variables; ++i) {
268 if (!IsConstant(i)) {
269 active_variables_.push_back(i);
270 active_variables_set_[i] =
true;
278 for (
const std::vector<int>& var_in_constraint : constraint_to_var_) {
279 if (var_in_constraint.size() <= 1)
continue;
280 for (
int i = 1; i < var_in_constraint.size(); ++i) {
281 union_find.
AddEdge(var_in_constraint[0], var_in_constraint[i]);
287 if (ObjectiveDomainIsConstraining()) {
288 const auto& refs = model_proto_.objective().vars();
289 const int num_terms = refs.size();
290 for (
int i = 1; i < num_terms; ++i) {
301 var_to_component_index_.assign(num_variables, -1);
302 for (
int var = 0;
var < num_variables; ++
var) {
303 if (IsConstant(
var))
continue;
305 CHECK_LT(root, var_to_component_index_.size());
306 int&
index = var_to_component_index_[root];
308 index = components_.size();
309 components_.push_back({});
311 var_to_component_index_[
var] =
index;
321 std::vector<int> component_sizes;
322 for (
const std::vector<int>& component : components_) {
323 component_sizes.push_back(component.size());
325 std::sort(component_sizes.begin(), component_sizes.end(),
326 std::greater<int>());
327 std::string compo_message;
328 if (component_sizes.size() > 1) {
329 if (component_sizes.size() <= 10) {
331 absl::StrCat(
" compo:", absl::StrJoin(component_sizes,
","));
333 component_sizes.resize(10);
335 absl::StrCat(
" compo:", absl::StrJoin(component_sizes,
","),
",...");
340 absl::StrCat(
"var:", active_variables_.size(),
"/", num_variables,
341 " constraints:", simplied_model_proto_.constraints().size(),
342 "/", model_proto_.constraints().size(), compo_message),
343 &last_logging_time_);
347 return active_variables_set_[
var];
350bool NeighborhoodGeneratorHelper::IsConstant(
int var)
const {
351 return model_proto_with_only_variables_.variables(
var).domain_size() == 2 &&
352 model_proto_with_only_variables_.variables(
var).domain(0) ==
353 model_proto_with_only_variables_.variables(
var).domain(1);
361 absl::ReaderMutexLock lock(&domain_mutex_);
362 *neighborhood.
delta.mutable_variables() =
363 model_proto_with_only_variables_.variables();
375 const CpSolverResponse& initial_solution)
const {
376 std::vector<int> active_intervals;
377 absl::ReaderMutexLock lock(&domain_mutex_);
379 const ConstraintProto& interval_ct =
ModelProto().constraints(i);
383 if (interval_ct.enforcement_literal().size() == 1) {
384 const int enforcement_ref = interval_ct.enforcement_literal(0);
385 const int enforcement_var =
PositiveRef(enforcement_ref);
386 const int value = initial_solution.solution(enforcement_var);
394 if (interval_ct.enforcement_literal().empty()) {
395 bool is_constant =
true;
396 for (
const int v : interval_ct.interval().start().vars()) {
397 if (!IsConstant(v)) {
402 for (
const int v : interval_ct.interval().size().vars()) {
403 if (!IsConstant(v)) {
408 for (
const int v : interval_ct.interval().end().vars()) {
409 if (!IsConstant(v)) {
414 if (is_constant)
continue;
417 active_intervals.push_back(i);
419 return active_intervals;
423 const CpSolverResponse& initial_solution)
const {
424 struct HeadAndArcLiteral {
429 std::vector<std::vector<int>> result;
430 absl::flat_hash_map<int, HeadAndArcLiteral> tail_to_head_and_arc_literal;
433 const CircuitConstraintProto&
ct =
ModelProto().constraints(i).circuit();
437 tail_to_head_and_arc_literal.clear();
438 for (
int i = 0; i <
ct.literals_size(); ++i) {
440 const int head =
ct.heads(i);
441 const int tail =
ct.tails(i);
443 const int64_t
value = initial_solution.solution(bool_var);
448 tail_to_head_and_arc_literal[
tail] = {
head, bool_var};
451 if (tail_to_head_and_arc_literal.empty())
continue;
454 int current_node = min_node;
455 std::vector<int> path;
457 auto it = tail_to_head_and_arc_literal.find(current_node);
458 CHECK(it != tail_to_head_and_arc_literal.end());
459 current_node = it->second.head;
460 path.push_back(it->second.literal);
461 }
while (current_node != min_node);
462 result.push_back(std::move(path));
465 std::vector<HeadAndArcLiteral> route_starts;
467 const RoutesConstraintProto&
ct =
ModelProto().constraints(i).routes();
468 tail_to_head_and_arc_literal.clear();
469 route_starts.clear();
472 for (
int i = 0; i <
ct.literals_size(); ++i) {
474 const int head =
ct.heads(i);
475 const int tail =
ct.tails(i);
477 const int64_t
value = initial_solution.solution(bool_var);
483 route_starts.push_back({
head, bool_var});
485 tail_to_head_and_arc_literal[
tail] = {
head, bool_var};
490 for (
const HeadAndArcLiteral& head_var : route_starts) {
491 std::vector<int> path;
492 int current_node = head_var.head;
493 path.push_back(head_var.literal);
495 auto it = tail_to_head_and_arc_literal.find(current_node);
496 CHECK(it != tail_to_head_and_arc_literal.end());
497 current_node = it->second.head;
498 path.push_back(it->second.literal);
499 }
while (current_node != 0);
500 result.push_back(std::move(path));
508 const CpSolverResponse& base_solution,
509 const absl::flat_hash_set<int>& variables_to_fix)
const {
514 absl::ReaderMutexLock domain_lock(&domain_mutex_);
516 const int num_variables =
517 model_proto_with_only_variables_.variables().size();
518 neighborhood.
delta.mutable_variables()->Reserve(num_variables);
519 for (
int i = 0; i < num_variables; ++i) {
520 const IntegerVariableProto& current_var =
521 model_proto_with_only_variables_.variables(i);
522 IntegerVariableProto* new_var = neighborhood.
delta.add_variables();
525 if (
DEBUG_MODE) new_var->set_name(current_var.name());
528 const int64_t base_value = base_solution.solution(i);
544 }
else if (variables_to_fix.contains(i)) {
545 new_var->add_domain(base_value);
546 new_var->add_domain(base_value);
558 std::vector<int> count(components_.size(), 0);
559 const int num_variables = neighborhood.
delta.variables().size();
560 for (
int var = 0;
var < num_variables; ++
var) {
561 const auto& domain = neighborhood.
delta.variables(
var).domain();
562 if (domain.size() != 2 || domain[0] != domain[1]) {
564 if (is_in_objective_[
var]) {
567 const int c = var_to_component_index_[
var];
568 if (c != -1) count[c]++;
572 for (
int i = 0; i < components_.size(); ++i) {
573 if (count[i] == components_[i].size()) {
576 components_[i].begin(), components_[i].end());
586 if (model_proto_.has_objective() &&
587 (model_proto_.objective().domain().size() != 2 ||
589 model_proto_.objective().domain(0))) {
596 neighborhood.
is_reduced = !variables_to_fix.empty();
607 const CpSolverResponse& initial_solution, CpModelProto*
model_proto)
const {
611 const IntegerVariableProto& var_proto =
model_proto->variables(
var);
612 return var_proto.domain_size() == 2 &&
613 var_proto.domain(0) == var_proto.domain(1);
616 if (is_fixed(
var))
continue;
620 initial_solution.solution(
var));
625 const std::vector<int>& constraints_to_remove)
const {
628 if (constraints_to_remove.empty())
return neighborhood;
635 const CpSolverResponse& initial_solution,
636 const std::vector<int>& relaxed_variables)
const {
637 std::vector<bool> relaxed_variables_set(model_proto_.variables_size(),
false);
638 for (
const int var : relaxed_variables) relaxed_variables_set[
var] =
true;
639 absl::flat_hash_set<int> fixed_variables;
642 for (
const int i : active_variables_) {
643 if (!relaxed_variables_set[i]) {
644 fixed_variables.insert(i);
652 const CpSolverResponse& initial_solution)
const {
654 const absl::flat_hash_set<int> fixed_variables(all_variables.begin(),
655 all_variables.end());
666 if (num_calls_ <= 10)
return std::numeric_limits<double>::infinity();
667 return current_average_ + sqrt((2 * log(total_num_calls)) / num_calls_);
675 std::sort(solve_data_.begin(), solve_data_.end());
678 int num_fully_solved_in_batch = 0;
679 int num_not_fully_solved_in_batch = 0;
681 for (
const SolveData& data : solve_data_) {
688 if (data.status == CpSolverStatus::INFEASIBLE ||
689 data.status == CpSolverStatus::OPTIMAL) {
690 ++num_fully_solved_calls_;
691 ++num_fully_solved_in_batch;
693 ++num_not_fully_solved_in_batch;
702 const IntegerValue best_objective_improvement =
704 ? IntegerValue(
CapSub(data.new_objective_bound.value(),
705 data.initial_best_objective_bound.value()))
706 : IntegerValue(
CapSub(data.initial_best_objective.value(),
707 data.new_objective.value()));
708 if (best_objective_improvement > 0) {
709 num_consecutive_non_improving_calls_ = 0;
711 ++num_consecutive_non_improving_calls_;
716 const double gain_per_time_unit =
717 std::max(0.0,
static_cast<double>(best_objective_improvement.value())) /
718 (1.0 + data.deterministic_time);
719 if (num_calls_ <= 100) {
720 current_average_ += (gain_per_time_unit - current_average_) / num_calls_;
722 current_average_ = 0.9 * current_average_ + 0.1 * gain_per_time_unit;
725 deterministic_time_ += data.deterministic_time;
729 difficulty_.
Update(num_not_fully_solved_in_batch,
730 num_fully_solved_in_batch);
738 if (num_consecutive_non_improving_calls_ > 50) {
739 num_consecutive_non_improving_calls_ = 0;
740 deterministic_limit_ *= 1.02;
744 deterministic_limit_ =
std::min(60.0, deterministic_limit_);
752void GetRandomSubset(
double relative_size, std::vector<int>* base,
753 absl::BitGenRef random) {
754 if (base->empty())
return;
758 std::shuffle(base->begin(), base->end(), random);
759 const int target_size = std::round(relative_size * base->size());
760 base->resize(target_size);
766 const CpSolverResponse& initial_solution,
double difficulty,
767 absl::BitGenRef random) {
769 GetRandomSubset(1.0 -
difficulty, &fixed_variables, random);
771 initial_solution, {fixed_variables.begin(), fixed_variables.end()});
775 const CpSolverResponse& initial_solution,
double difficulty,
776 absl::BitGenRef random) {
781 std::vector<int> relaxed_variables;
785 std::vector<int> active_constraints(num_active_constraints);
786 for (
int c = 0; c < num_active_constraints; ++c) {
787 active_constraints[c] = c;
789 std::shuffle(active_constraints.begin(), active_constraints.end(), random);
792 std::vector<bool> visited_variables_set(num_model_vars,
false);
794 const int num_active_vars =
796 const int target_size = std::ceil(
difficulty * num_active_vars);
799 for (
const int constraint_index : active_constraints) {
801 if (visited_variables_set[
var])
continue;
802 visited_variables_set[
var] =
true;
804 relaxed_variables.push_back(
var);
805 if (relaxed_variables.size() == target_size)
break;
808 if (relaxed_variables.size() == target_size)
break;
816 const CpSolverResponse& initial_solution,
double difficulty,
817 absl::BitGenRef random) {
823 std::vector<bool> visited_variables_set(num_model_vars,
false);
824 std::vector<int> relaxed_variables;
825 std::vector<int> visited_variables;
829 std::vector<bool> scanned_constraints(num_model_constraints,
false);
831 std::vector<int> random_variables;
837 const int num_active_vars =
839 const int target_size = std::ceil(
difficulty * num_active_vars);
842 const int first_var =
844 random, 0, num_active_vars)];
846 visited_variables_set[first_var] =
true;
847 visited_variables.push_back(first_var);
848 relaxed_variables.push_back(first_var);
850 for (
int i = 0; i < visited_variables.size(); ++i) {
851 random_variables.clear();
855 if (scanned_constraints[
ct])
continue;
856 scanned_constraints[
ct] =
true;
858 if (visited_variables_set[
var])
continue;
859 visited_variables_set[
var] =
true;
860 random_variables.push_back(
var);
865 std::shuffle(random_variables.begin(), random_variables.end(), random);
866 for (
const int var : random_variables) {
867 if (relaxed_variables.size() < target_size) {
868 visited_variables.push_back(
var);
870 relaxed_variables.push_back(
var);
876 if (relaxed_variables.size() >= target_size)
break;
883 const CpSolverResponse& initial_solution,
double difficulty,
884 absl::BitGenRef random) {
886 if (num_model_constraints == 0 ||
892 std::vector<bool> visited_variables_set(num_model_vars,
false);
893 std::vector<int> relaxed_variables;
895 std::vector<bool> added_constraints(num_model_constraints,
false);
896 std::vector<int> next_constraints;
898 std::vector<int> random_variables;
901 const int num_active_vars =
903 const int target_size = std::ceil(
difficulty * num_active_vars);
908 if (num_active_constraints != 0) {
909 next_constraints.push_back(
910 absl::Uniform<int>(random, 0, num_active_constraints));
911 added_constraints[next_constraints.back()] =
true;
914 while (relaxed_variables.size() < target_size) {
916 if (next_constraints.empty())
break;
919 const int i = absl::Uniform<int>(random, 0, next_constraints.size());
920 const int constraint_index = next_constraints[i];
921 std::swap(next_constraints[i], next_constraints.back());
922 next_constraints.pop_back();
926 CHECK_LT(constraint_index, num_active_constraints);
928 std::shuffle(random_variables.begin(), random_variables.end(), random);
929 for (
const int var : random_variables) {
930 if (visited_variables_set[
var])
continue;
931 visited_variables_set[
var] =
true;
933 relaxed_variables.push_back(
var);
935 if (relaxed_variables.size() == target_size)
break;
938 if (added_constraints[
ct])
continue;
939 added_constraints[
ct] =
true;
940 next_constraints.push_back(
ct);
950int64_t GetLinearExpressionValue(
const LinearExpressionProto& expr,
951 const CpSolverResponse& initial_solution) {
952 int64_t result = expr.offset();
953 for (
int i = 0; i < expr.vars_size(); ++i) {
954 result += expr.coeffs(i) * initial_solution.solution(expr.vars(i));
959void AddLinearExpressionToConstraint(
const int64_t
coeff,
960 const LinearExpressionProto& expr,
961 LinearConstraintProto* constraint,
962 int64_t* rhs_offset) {
963 *rhs_offset -=
coeff * expr.offset();
964 for (
int i = 0; i < expr.vars_size(); ++i) {
965 constraint->add_vars(expr.vars(i));
966 constraint->add_coeffs(expr.coeffs(i) *
coeff);
970void AddPrecedenceConstraints(
const absl::Span<const int> intervals,
971 const absl::flat_hash_set<int>& ignored_intervals,
972 const CpSolverResponse& initial_solution,
973 const NeighborhoodGeneratorHelper& helper,
974 Neighborhood* neighborhood) {
977 std::vector<std::pair<int64_t, int>> start_interval_pairs;
978 for (
const int i : intervals) {
979 if (ignored_intervals.contains(i))
continue;
980 const ConstraintProto& interval_ct = helper.ModelProto().constraints(i);
983 const LinearExpressionProto& size_var = interval_ct.interval().size();
984 if (GetLinearExpressionValue(size_var, initial_solution) == 0)
continue;
986 const LinearExpressionProto& start_var = interval_ct.interval().start();
987 const int64_t start_value =
988 GetLinearExpressionValue(start_var, initial_solution);
990 start_interval_pairs.push_back({start_value, i});
992 std::sort(start_interval_pairs.begin(), start_interval_pairs.end());
995 for (
int i = 0; i + 1 < start_interval_pairs.size(); ++i) {
996 const LinearExpressionProto& before_start =
998 .constraints(start_interval_pairs[i].second)
1001 const LinearExpressionProto& before_end =
1003 .constraints(start_interval_pairs[i].second)
1006 const LinearExpressionProto& after_start =
1008 .constraints(start_interval_pairs[i + 1].second)
1014 LinearConstraintProto* linear =
1015 neighborhood->delta.add_constraints()->mutable_linear();
1017 int64_t rhs_offset = 0;
1018 if (GetLinearExpressionValue(before_end, initial_solution) <=
1019 GetLinearExpressionValue(after_start, initial_solution)) {
1021 AddLinearExpressionToConstraint(1, before_end, linear, &rhs_offset);
1025 rhs_offset = GetLinearExpressionValue(before_start, initial_solution) -
1026 GetLinearExpressionValue(after_start, initial_solution);
1027 AddLinearExpressionToConstraint(1, before_start, linear, &rhs_offset);
1030 AddLinearExpressionToConstraint(-1, after_start, linear, &rhs_offset);
1031 linear->add_domain(rhs_offset);
1035 int64_t activity = 0;
1036 for (
int i = 0; i < linear->vars().size(); ++i) {
1038 linear->coeffs(i) * initial_solution.solution(linear->vars(i));
1040 CHECK_GE(activity, linear->domain(0));
1041 CHECK_LE(activity, linear->domain(1));
1048 const absl::Span<const int> intervals_to_relax,
1049 const CpSolverResponse& initial_solution,
1053 (intervals_to_relax.size() <
1057 absl::flat_hash_set<int> ignored_intervals(intervals_to_relax.begin(),
1058 intervals_to_relax.end());
1063 if (ignored_intervals.contains(i))
continue;
1065 const ConstraintProto& interval_ct = helper.
ModelProto().constraints(i);
1066 if (interval_ct.enforcement_literal().empty())
continue;
1068 CHECK_EQ(interval_ct.enforcement_literal().size(), 1);
1069 const int enforcement_ref = interval_ct.enforcement_literal(0);
1070 const int enforcement_var =
PositiveRef(enforcement_ref);
1071 const int value = initial_solution.solution(enforcement_var);
1079 ignored_intervals.insert(i);
1084 neighborhood.
delta.mutable_variables(enforcement_var)->clear_domain();
1085 neighborhood.
delta.mutable_variables(enforcement_var)->add_domain(
value);
1086 neighborhood.
delta.mutable_variables(enforcement_var)->add_domain(
value);
1090 AddPrecedenceConstraints(
1091 helper.
ModelProto().constraints(c).no_overlap().intervals(),
1092 ignored_intervals, initial_solution, helper, &neighborhood);
1095 AddPrecedenceConstraints(
1096 helper.
ModelProto().constraints(c).cumulative().intervals(),
1097 ignored_intervals, initial_solution, helper, &neighborhood);
1100 AddPrecedenceConstraints(
1101 helper.
ModelProto().constraints(c).no_overlap_2d().x_intervals(),
1102 ignored_intervals, initial_solution, helper, &neighborhood);
1103 AddPrecedenceConstraints(
1104 helper.
ModelProto().constraints(c).no_overlap_2d().y_intervals(),
1105 ignored_intervals, initial_solution, helper, &neighborhood);
1112 return neighborhood;
1116 const CpSolverResponse& initial_solution,
double difficulty,
1117 absl::BitGenRef random) {
1118 std::vector<int> intervals_to_relax =
1120 GetRandomSubset(
difficulty, &intervals_to_relax, random);
1127 const CpSolverResponse& initial_solution,
double difficulty,
1128 absl::BitGenRef random) {
1129 std::vector<std::pair<int64_t, int>> start_interval_pairs;
1130 const std::vector<int> active_intervals =
1132 std::vector<int> intervals_to_relax;
1136 for (
const int i : active_intervals) {
1138 const LinearExpressionProto& start_var = interval_ct.interval().start();
1139 const int64_t start_value =
1140 GetLinearExpressionValue(start_var, initial_solution);
1141 start_interval_pairs.push_back({start_value, i});
1143 std::sort(start_interval_pairs.begin(), start_interval_pairs.end());
1144 const int relaxed_size = std::floor(
difficulty * start_interval_pairs.size());
1146 std::uniform_int_distribution<int> random_var(
1147 0, start_interval_pairs.size() - relaxed_size - 1);
1148 const int random_start_index = random_var(random);
1152 for (
int i = random_start_index; i < relaxed_size; ++i) {
1153 intervals_to_relax.push_back(start_interval_pairs[i].second);
1161 const CpSolverResponse& initial_solution,
double difficulty,
1162 absl::BitGenRef random) {
1163 const std::vector<std::vector<int>> all_paths =
1167 absl::flat_hash_set<int> all_path_variables;
1168 for (
auto& path : all_paths) {
1169 all_path_variables.insert(path.begin(), path.end());
1171 std::vector<int> fixed_variables(all_path_variables.begin(),
1172 all_path_variables.end());
1173 std::sort(fixed_variables.begin(), fixed_variables.end());
1174 GetRandomSubset(1.0 -
difficulty, &fixed_variables, random);
1176 initial_solution, {fixed_variables.begin(), fixed_variables.end()});
1180 const CpSolverResponse& initial_solution,
double difficulty,
1181 absl::BitGenRef random) {
1182 std::vector<std::vector<int>> all_paths =
1186 absl::flat_hash_set<int> all_path_variables;
1187 for (
const auto& path : all_paths) {
1188 all_path_variables.insert(path.begin(), path.end());
1192 const int num_variables_to_relax =
1193 static_cast<int>(all_path_variables.size() *
difficulty);
1194 absl::flat_hash_set<int> relaxed_variables;
1195 while (relaxed_variables.size() < num_variables_to_relax) {
1196 DCHECK(!all_paths.empty());
1197 const int path_index = absl::Uniform<int>(random, 0, all_paths.size());
1198 std::vector<int>& path = all_paths[path_index];
1199 const int path_size = path.size();
1200 const int segment_length =
1201 std::min(path_size, absl::Uniform<int>(random, 4, 8));
1202 const int segment_start =
1203 absl::Uniform<int>(random, 0, path_size - segment_length);
1204 for (
int i = segment_start; i < segment_start + segment_length; ++i) {
1205 relaxed_variables.insert(path[i]);
1209 path.erase(path.begin() + segment_start,
1210 path.begin() + segment_start + segment_length);
1212 std::swap(all_paths[path_index], all_paths.back());
1213 all_paths.pop_back();
1218 absl::flat_hash_set<int> fixed_variables;
1219 for (
const int var : all_path_variables) {
1220 if (!relaxed_variables.contains(
var)) fixed_variables.insert(
var);
1226 const CpSolverResponse& initial_solution,
double difficulty,
1227 absl::BitGenRef random) {
1228 std::vector<std::vector<int>> all_paths =
1231 if (all_paths.empty()) {
1236 absl::flat_hash_set<int> all_path_variables;
1237 for (
const auto& path : all_paths) {
1238 all_path_variables.insert(path.begin(), path.end());
1242 const int num_variables_to_relax =
1243 static_cast<int>(all_path_variables.size() *
difficulty);
1244 absl::flat_hash_set<int> relaxed_variables;
1247 for (
const auto& path : all_paths) {
1248 relaxed_variables.insert(path.front());
1249 relaxed_variables.insert(path.back());
1253 for (
auto& path : all_paths) {
1254 std::shuffle(path.begin(), path.end(), random);
1258 const int path_to_clean = absl::Uniform<int>(random, 0, all_paths.size());
1259 while (relaxed_variables.size() < num_variables_to_relax &&
1260 !all_paths[path_to_clean].empty()) {
1261 relaxed_variables.insert(all_paths[path_to_clean].back());
1262 all_paths[path_to_clean].pop_back();
1264 if (all_paths[path_to_clean].empty()) {
1265 std::swap(all_paths[path_to_clean], all_paths.back());
1266 all_paths.pop_back();
1270 while (relaxed_variables.size() < num_variables_to_relax) {
1271 DCHECK(!all_paths.empty());
1272 const int path_index = absl::Uniform<int>(random, 0, all_paths.size());
1273 relaxed_variables.insert(all_paths[path_index].back());
1276 all_paths[path_index].pop_back();
1277 if (all_paths[path_index].empty()) {
1278 std::swap(all_paths[path_index], all_paths.back());
1279 all_paths.pop_back();
1284 absl::flat_hash_set<int> fixed_variables;
1285 for (
const int var : all_path_variables) {
1286 if (!relaxed_variables.contains(
var)) fixed_variables.insert(
var);
1292 if (incomplete_solutions_ !=
nullptr) {
1296 if (response_manager_ !=
nullptr) {
1304 if (lp_solutions_ !=
nullptr && lp_solutions_->
NumSolutions() > 0) {
1308 if (relaxation_solutions_ !=
nullptr &&
1316 const CpSolverResponse& initial_solution,
double difficulty,
1317 absl::BitGenRef random) {
1321 const bool lp_solution_available =
1322 (lp_solutions_ !=
nullptr && lp_solutions_->
NumSolutions() > 0);
1324 const bool relaxation_solution_available =
1325 (relaxation_solutions_ !=
nullptr &&
1328 const bool incomplete_solution_available =
1329 (incomplete_solutions_ !=
nullptr &&
1332 if (!lp_solution_available && !relaxation_solution_available &&
1333 !incomplete_solution_available) {
1334 return neighborhood;
1341 std::bernoulli_distribution random_bool(0.5);
1342 const bool use_lp_relaxation =
1343 (lp_solution_available && relaxation_solution_available)
1344 ? random_bool(random)
1345 : lp_solution_available;
1346 if (use_lp_relaxation) {
1349 nullptr, lp_solutions_,
1350 incomplete_solutions_, random);
1352 incomplete_solution_available ?
"incomplete" :
"lp";
1354 CHECK(relaxation_solution_available || incomplete_solution_available);
1356 response_manager_, relaxation_solutions_,
1357 nullptr, incomplete_solutions_, random);
1359 incomplete_solution_available ?
"incomplete" :
"relaxation";
1364 return neighborhood;
1369 for (
const std::pair</*model_var*/ int, /*value*/ int64_t>& fixed_var :
1371 const int var = fixed_var.first;
1372 const int64_t
value = fixed_var.second;
1373 if (
var >= neighborhood.
delta.variables_size())
continue;
1378 return neighborhood;
1381 neighborhood.
delta.mutable_variables(
var)->clear_domain();
1382 neighborhood.
delta.mutable_variables(
var)->add_domain(
value);
1383 neighborhood.
delta.mutable_variables(
var)->add_domain(
value);
1387 for (
const std::pair<
int,
1388 std::pair<int64_t, int64_t>>& reduced_var :
1390 const int var = reduced_var.first;
1391 const int64_t lb = reduced_var.second.first;
1392 const int64_t ub = reduced_var.second.second;
1393 if (
var >= neighborhood.
delta.variables_size())
continue;
1399 return neighborhood;
1405 return neighborhood;
1409 const CpSolverResponse& initial_solution,
double difficulty,
1410 absl::BitGenRef random) {
1411 std::vector<int> removable_constraints;
1413 removable_constraints.reserve(num_constraints);
1414 for (
int c = 0; c < num_constraints; ++c) {
1418 ConstraintProto::kInterval) {
1421 removable_constraints.push_back(c);
1424 const int target_size =
1425 std::round((1.0 -
difficulty) * removable_constraints.size());
1427 const int random_start_index =
1428 absl::Uniform<int>(random, 0, removable_constraints.size());
1429 std::vector<int> removed_constraints;
1430 removed_constraints.reserve(target_size);
1431 int c = random_start_index;
1432 while (removed_constraints.size() < target_size) {
1433 removed_constraints.push_back(removable_constraints[c]);
1435 if (c == removable_constraints.size()) {
1447 std::vector<int> removable_constraints;
1449 constraint_weights_.reserve(num_constraints);
1451 for (
int c = 0; c < num_constraints; ++c) {
1453 case ConstraintProto::kCumulative:
1454 case ConstraintProto::kAllDiff:
1455 case ConstraintProto::kElement:
1456 case ConstraintProto::kRoutes:
1457 case ConstraintProto::kCircuit:
1458 constraint_weights_.push_back(3.0);
1459 num_removable_constraints_++;
1461 case ConstraintProto::kBoolOr:
1462 case ConstraintProto::kBoolAnd:
1463 case ConstraintProto::kBoolXor:
1464 case ConstraintProto::kIntProd:
1465 case ConstraintProto::kIntDiv:
1466 case ConstraintProto::kIntMod:
1467 case ConstraintProto::kLinMax:
1468 case ConstraintProto::kNoOverlap:
1469 case ConstraintProto::kNoOverlap2D:
1470 constraint_weights_.push_back(2.0);
1471 num_removable_constraints_++;
1473 case ConstraintProto::kLinear:
1474 case ConstraintProto::kTable:
1475 case ConstraintProto::kAutomaton:
1476 case ConstraintProto::kInverse:
1477 case ConstraintProto::kReservoir:
1478 case ConstraintProto::kAtMostOne:
1479 case ConstraintProto::kExactlyOne:
1480 constraint_weights_.push_back(1.0);
1481 num_removable_constraints_++;
1483 case ConstraintProto::CONSTRAINT_NOT_SET:
1484 case ConstraintProto::kDummyConstraint:
1485 case ConstraintProto::kInterval:
1488 constraint_weights_.push_back(0.0);
1494void WeightedRandomRelaxationNeighborhoodGenerator::
1495 AdditionalProcessingOnSynchronize(
const SolveData& solve_data) {
1496 const IntegerValue best_objective_improvement =
1497 solve_data.new_objective_bound - solve_data.initial_best_objective_bound;
1499 const std::vector<int>& removed_constraints =
1500 removed_constraints_[solve_data.neighborhood_id];
1514 if (best_objective_improvement > 0) {
1516 for (
int c : removed_constraints) {
1517 if (constraint_weights_[c] <= 90.0) {
1518 constraint_weights_[c] += 10.0;
1520 constraint_weights_[c] = 100.0;
1523 }
else if (solve_data.status == CpSolverStatus::OPTIMAL &&
1524 best_objective_improvement < 0) {
1526 for (
int c : removed_constraints) {
1527 if (constraint_weights_[c] > 0.5) {
1528 constraint_weights_[c] -= 0.5;
1532 removed_constraints_.erase(solve_data.neighborhood_id);
1536 const CpSolverResponse& initial_solution,
double difficulty,
1537 absl::BitGenRef random) {
1538 const int target_size =
1539 std::round((1.0 -
difficulty) * num_removable_constraints_);
1541 std::vector<int> removed_constraints;
1546 std::vector<std::pair<double, int>> constraint_removal_scores;
1547 std::uniform_real_distribution<double> random_var(0.0, 1.0);
1548 for (
int c = 0; c < constraint_weights_.size(); ++c) {
1549 if (constraint_weights_[c] <= 0)
continue;
1550 const double u = random_var(random);
1551 const double score = std::pow(u, (1 / constraint_weights_[c]));
1552 constraint_removal_scores.push_back({score, c});
1554 std::sort(constraint_removal_scores.rbegin(),
1555 constraint_removal_scores.rend());
1556 for (
int i = 0; i < target_size; ++i) {
1557 removed_constraints.push_back(constraint_removal_scores[i].second);
1563 result.
id = next_available_id_;
1564 next_available_id_++;
1565 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
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_
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
void LogPeriodicMessage(const std::string &prefix, const std::string &message, absl::Time *last_logging_time)
bool LoggingIsEnabled() const
const SharedSolutionRepository< int64_t > & SolutionsRepository() const
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)