39 shared_time_limit_(shared_time_limit),
40 shared_bounds_(shared_bounds),
41 shared_response_(shared_response) {
42 CHECK(shared_response_ !=
nullptr);
43 if (shared_bounds_ !=
nullptr) {
46 *model_proto_with_only_variables_.mutable_variables() =
47 model_proto_.variables();
48 RecomputeHelperData();
53 absl::MutexLock mutex_lock(&mutex_);
54 if (shared_bounds_ !=
nullptr) {
55 std::vector<int> model_variables;
56 std::vector<int64> new_lower_bounds;
57 std::vector<int64> new_upper_bounds;
59 &new_lower_bounds, &new_upper_bounds);
61 for (
int i = 0; i < model_variables.size(); ++i) {
62 const int var = model_variables[i];
63 const int64 new_lb = new_lower_bounds[i];
64 const int64 new_ub = new_upper_bounds[i];
67 model_proto_with_only_variables_.variables(
var).domain();
68 const int64 old_lb = domain.Get(0);
69 const int64 old_ub = domain.Get(domain.size() - 1);
70 VLOG(3) <<
"Variable: " <<
var <<
" old domain: [" << old_lb <<
", "
71 << old_ub <<
"] new domain: [" << new_lb <<
", " << new_ub
95 new_domain, model_proto_with_only_variables_.mutable_variables(
var));
99 if (!model_variables.empty()) {
100 RecomputeHelperData();
105 void NeighborhoodGeneratorHelper::RecomputeHelperData() {
112 var_to_constraint_.assign(model_proto_.variables_size(), {});
113 constraint_to_var_.assign(model_proto_.constraints_size(), {});
114 for (
int ct_index = 0; ct_index < model_proto_.constraints_size();
117 if (IsConstant(
var))
continue;
118 var_to_constraint_[
var].push_back(ct_index);
119 constraint_to_var_[ct_index].push_back(
var);
125 type_to_constraints_.clear();
126 const int num_constraints = model_proto_.constraints_size();
127 for (
int c = 0; c < num_constraints; ++c) {
128 const int type = model_proto_.constraints(c).constraint_case();
129 if (type >= type_to_constraints_.size()) {
130 type_to_constraints_.resize(type + 1);
132 type_to_constraints_[type].push_back(c);
135 active_variables_.clear();
136 active_variables_set_.assign(model_proto_.variables_size(),
false);
138 if (parameters_.lns_focus_on_decision_variables()) {
139 for (
const auto& search_strategy : model_proto_.search_strategy()) {
140 for (
const int var : search_strategy.variables()) {
142 if (!active_variables_set_[pos_var] && !IsConstant(pos_var)) {
143 active_variables_set_[pos_var] =
true;
144 active_variables_.push_back(pos_var);
150 if (!active_variables_.empty())
return;
154 for (
int i = 0; i < model_proto_.variables_size(); ++i) {
155 if (!IsConstant(i)) {
156 active_variables_.push_back(i);
157 active_variables_set_[i] =
true;
163 return active_variables_set_[
var];
166 bool NeighborhoodGeneratorHelper::IsConstant(
int var)
const {
167 return model_proto_with_only_variables_.variables(
var).domain_size() == 2 &&
168 model_proto_with_only_variables_.variables(
var).domain(0) ==
169 model_proto_with_only_variables_.variables(
var).domain(1);
176 neighborhood.
cp_model = model_proto_;
177 *neighborhood.
cp_model.mutable_variables() =
178 model_proto_with_only_variables_.variables();
183 const CpSolverResponse& initial_solution,
184 const std::vector<int>& variables_to_fix)
const {
190 neighborhood.
cp_model.clear_solution_hint();
192 neighborhood.
cp_model.mutable_solution_hint()->add_vars(
var);
193 neighborhood.
cp_model.mutable_solution_hint()->add_values(
194 initial_solution.solution(
var));
197 neighborhood.
is_reduced = !variables_to_fix.empty();
198 if (!neighborhood.
is_reduced)
return neighborhood;
199 CHECK_EQ(initial_solution.solution_size(),
200 neighborhood.
cp_model.variables_size());
201 for (
const int var : variables_to_fix) {
202 neighborhood.
cp_model.mutable_variables(
var)->clear_domain();
203 neighborhood.
cp_model.mutable_variables(
var)->add_domain(
204 initial_solution.solution(
var));
205 neighborhood.
cp_model.mutable_variables(
var)->add_domain(
206 initial_solution.solution(
var));
217 const std::vector<int>& constraints_to_remove)
const {
222 if (constraints_to_remove.empty())
return neighborhood;
224 for (
const int constraint : constraints_to_remove) {
225 neighborhood.
cp_model.mutable_constraints(constraint)->Clear();
232 const CpSolverResponse& initial_solution,
233 const std::vector<int>& relaxed_variables)
const {
234 std::vector<bool> relaxed_variables_set(model_proto_.variables_size(),
false);
235 for (
const int var : relaxed_variables) relaxed_variables_set[
var] =
true;
236 std::vector<int> fixed_variables;
237 for (
const int i : active_variables_) {
238 if (!relaxed_variables_set[i]) {
239 fixed_variables.push_back(i);
246 const CpSolverResponse& initial_solution)
const {
247 std::vector<int> fixed_variables;
248 for (
const int i : active_variables_) {
249 fixed_variables.push_back(i);
259 absl::MutexLock mutex_lock(&
mutex_);
261 if (num_calls_ <= 10)
return std::numeric_limits<double>::infinity();
262 return current_average_ + sqrt((2 * log(total_num_calls)) / num_calls_);
266 absl::MutexLock mutex_lock(&
mutex_);
270 std::sort(solve_data_.begin(), solve_data_.end());
273 int num_fully_solved_in_batch = 0;
274 int num_not_fully_solved_in_batch = 0;
276 for (
const SolveData& data : solve_data_) {
285 ++num_fully_solved_calls_;
286 ++num_fully_solved_in_batch;
288 ++num_not_fully_solved_in_batch;
297 const IntegerValue best_objective_improvement =
299 ? IntegerValue(
CapSub(data.new_objective_bound.value(),
300 data.initial_best_objective_bound.value()))
301 : IntegerValue(
CapSub(data.initial_best_objective.value(),
302 data.new_objective.value()));
303 if (best_objective_improvement > 0) {
304 num_consecutive_non_improving_calls_ = 0;
306 ++num_consecutive_non_improving_calls_;
311 const double gain_per_time_unit =
312 std::max(0.0,
static_cast<double>(best_objective_improvement.value())) /
313 (1.0 + data.deterministic_time);
314 if (num_calls_ <= 100) {
315 current_average_ += (gain_per_time_unit - current_average_) / num_calls_;
317 current_average_ = 0.9 * current_average_ + 0.1 * gain_per_time_unit;
320 deterministic_time_ += data.deterministic_time;
324 difficulty_.
Update(num_not_fully_solved_in_batch,
325 num_fully_solved_in_batch);
333 if (num_consecutive_non_improving_calls_ > 50) {
334 num_consecutive_non_improving_calls_ = 0;
335 deterministic_limit_ *= 1.02;
339 deterministic_limit_ =
std::min(60.0, deterministic_limit_);
347 void GetRandomSubset(
double relative_size, std::vector<int>* base,
348 absl::BitGenRef random) {
351 std::shuffle(base->begin(), base->end(), random);
352 const int target_size = std::round(relative_size * base->size());
353 base->resize(target_size);
359 const CpSolverResponse& initial_solution,
double difficulty,
360 absl::BitGenRef random) {
362 GetRandomSubset(1.0 -
difficulty, &fixed_variables, random);
367 const CpSolverResponse& initial_solution,
double difficulty,
368 absl::BitGenRef random) {
371 const int target_size = std::ceil(
difficulty * num_active_vars);
372 if (target_size == num_active_vars) {
377 std::vector<bool> visited_variables_set(num_model_vars,
false);
378 std::vector<int> relaxed_variables;
379 std::vector<int> visited_variables;
381 const int first_var =
383 visited_variables_set[first_var] =
true;
384 visited_variables.push_back(first_var);
385 relaxed_variables.push_back(first_var);
387 std::vector<int> random_variables;
388 for (
int i = 0; i < visited_variables.size(); ++i) {
389 random_variables.clear();
394 if (visited_variables_set[
var])
continue;
395 visited_variables_set[
var] =
true;
396 random_variables.push_back(
var);
400 std::shuffle(random_variables.begin(), random_variables.end(), random);
401 for (
const int var : random_variables) {
402 if (relaxed_variables.size() < target_size) {
403 visited_variables.push_back(
var);
405 relaxed_variables.push_back(
var);
411 if (relaxed_variables.size() >= target_size)
break;
418 const CpSolverResponse& initial_solution,
double difficulty,
419 absl::BitGenRef random) {
422 const int target_size = std::ceil(
difficulty * num_active_vars);
424 if (num_constraints == 0 || target_size == num_active_vars) {
429 std::vector<bool> visited_variables_set(num_model_vars,
false);
430 std::vector<int> relaxed_variables;
431 std::vector<bool> added_constraints(num_constraints,
false);
432 std::vector<int> next_constraints;
435 next_constraints.push_back(absl::Uniform<int>(random, 0, num_constraints));
436 added_constraints[next_constraints.back()] =
true;
438 std::vector<int> random_variables;
439 while (relaxed_variables.size() < target_size) {
441 if (next_constraints.empty())
break;
444 const int i = absl::Uniform<int>(random, 0, next_constraints.size());
445 const int contraint_index = next_constraints[i];
446 std::swap(next_constraints[i], next_constraints.back());
447 next_constraints.pop_back();
451 CHECK_LT(contraint_index, num_constraints);
453 std::shuffle(random_variables.begin(), random_variables.end(), random);
454 for (
const int var : random_variables) {
455 if (visited_variables_set[
var])
continue;
456 visited_variables_set[
var] =
true;
458 relaxed_variables.push_back(
var);
460 if (relaxed_variables.size() == target_size)
break;
463 if (added_constraints[
ct])
continue;
464 added_constraints[
ct] =
true;
465 next_constraints.push_back(
ct);
473 const absl::Span<const int> intervals_to_relax,
474 const CpSolverResponse& initial_solution,
478 (intervals_to_relax.size() <
482 std::set<int> ignored_intervals(intervals_to_relax.begin(),
483 intervals_to_relax.end());
487 if (ignored_intervals.count(i))
continue;
489 const ConstraintProto& interval_ct = neighborhood.
cp_model.constraints(i);
490 if (interval_ct.enforcement_literal().empty())
continue;
492 CHECK_EQ(interval_ct.enforcement_literal().size(), 1);
493 const int enforcement_ref = interval_ct.enforcement_literal(0);
494 const int enforcement_var =
PositiveRef(enforcement_ref);
495 const int value = initial_solution.solution(enforcement_var);
498 neighborhood.
cp_model.mutable_variables(enforcement_var)->clear_domain();
499 neighborhood.
cp_model.mutable_variables(enforcement_var)->add_domain(
value);
500 neighborhood.
cp_model.mutable_variables(enforcement_var)->add_domain(
value);
505 ignored_intervals.insert(i);
511 std::vector<std::pair<int64, int>> start_interval_pairs;
513 neighborhood.
cp_model.constraints(c).no_overlap().intervals()) {
514 if (ignored_intervals.count(i))
continue;
515 const ConstraintProto& interval_ct = neighborhood.
cp_model.constraints(i);
518 const int size_var = interval_ct.interval().size();
519 if (initial_solution.solution(size_var) == 0)
continue;
521 const int start_var = interval_ct.interval().start();
522 const int64 start_value = initial_solution.solution(start_var);
523 start_interval_pairs.push_back({start_value, i});
525 std::sort(start_interval_pairs.begin(), start_interval_pairs.end());
528 for (
int i = 0; i + 1 < start_interval_pairs.size(); ++i) {
529 const int before_var =
530 neighborhood.
cp_model.constraints(start_interval_pairs[i].second)
533 const int after_var =
534 neighborhood.
cp_model.constraints(start_interval_pairs[i + 1].second)
537 CHECK_LE(initial_solution.solution(before_var),
538 initial_solution.solution(after_var));
540 LinearConstraintProto* linear =
541 neighborhood.
cp_model.add_constraints()->mutable_linear();
543 linear->add_domain(0);
544 linear->add_vars(before_var);
545 linear->add_coeffs(1);
546 linear->add_vars(after_var);
547 linear->add_coeffs(-1);
554 neighborhood.
cp_model.clear_solution_hint();
556 neighborhood.
cp_model.mutable_solution_hint()->add_vars(
var);
557 neighborhood.
cp_model.mutable_solution_hint()->add_values(
558 initial_solution.solution(
var));
566 const CpSolverResponse& initial_solution,
double difficulty,
567 absl::BitGenRef random) {
569 std::vector<int> intervals_to_relax(span.begin(), span.end());
570 GetRandomSubset(
difficulty, &intervals_to_relax, random);
577 const CpSolverResponse& initial_solution,
double difficulty,
578 absl::BitGenRef random) {
579 std::vector<std::pair<int64, int>> start_interval_pairs;
583 const int start_var = interval_ct.interval().start();
584 const int64 start_value = initial_solution.solution(start_var);
585 start_interval_pairs.push_back({start_value, i});
587 std::sort(start_interval_pairs.begin(), start_interval_pairs.end());
588 const int relaxed_size = std::floor(
difficulty * start_interval_pairs.size());
590 std::uniform_int_distribution<int> random_var(
591 0, start_interval_pairs.size() - relaxed_size - 1);
592 const int random_start_index = random_var(random);
593 std::vector<int> intervals_to_relax;
596 for (
int i = random_start_index; i < relaxed_size; ++i) {
597 intervals_to_relax.push_back(start_interval_pairs[i].second);
604 if (incomplete_solutions_ !=
nullptr) {
608 if (response_manager_ !=
nullptr) {
616 if (lp_solutions_ !=
nullptr && lp_solutions_->
NumSolutions() > 0) {
620 if (relaxation_solutions_ !=
nullptr &&
628 const CpSolverResponse& initial_solution,
double difficulty,
629 absl::BitGenRef random) {
633 const bool lp_solution_available =
634 (lp_solutions_ !=
nullptr && lp_solutions_->
NumSolutions() > 0);
636 const bool relaxation_solution_available =
637 (relaxation_solutions_ !=
nullptr &&
640 const bool incomplete_solution_available =
641 (incomplete_solutions_ !=
nullptr &&
644 if (!lp_solution_available && !relaxation_solution_available &&
645 !incomplete_solution_available) {
653 std::bernoulli_distribution random_bool(0.5);
654 const bool use_lp_relaxation =
655 (lp_solution_available && relaxation_solution_available)
656 ? random_bool(random)
657 : lp_solution_available;
658 if (use_lp_relaxation) {
661 nullptr, lp_solutions_,
662 incomplete_solutions_, random);
664 incomplete_solution_available ?
"incomplete" :
"lp";
666 CHECK(relaxation_solution_available || incomplete_solution_available);
668 response_manager_, relaxation_solutions_,
669 nullptr, incomplete_solutions_, random);
671 incomplete_solution_available ?
"incomplete" :
"relaxation";
680 for (
const std::pair</*model_var*/ int, /*value*/ int64> fixed_var :
682 const int var = fixed_var.first;
684 if (
var >= neighborhood.
cp_model.variables_size())
continue;
694 neighborhood.
cp_model.mutable_variables(
var)->clear_domain();
700 for (
const std::pair<
int, std::pair<int64, int64>>
702 const int var = reduced_var.first;
703 const int64 lb = reduced_var.second.first;
704 const int64 ub = reduced_var.second.second;
705 if (
var >= neighborhood.
cp_model.variables_size())
continue;
721 const CpSolverResponse& initial_solution,
double difficulty,
722 absl::BitGenRef random) {
723 std::vector<int> removable_constraints;
725 removable_constraints.reserve(num_constraints);
726 for (
int c = 0; c < num_constraints; ++c) {
730 ConstraintProto::kInterval) {
733 removable_constraints.push_back(c);
736 const int target_size =
737 std::round((1.0 -
difficulty) * removable_constraints.size());
739 const int random_start_index =
740 absl::Uniform<int>(random, 0, removable_constraints.size());
741 std::vector<int> removed_constraints;
742 removed_constraints.reserve(target_size);
743 int c = random_start_index;
744 while (removed_constraints.size() < target_size) {
745 removed_constraints.push_back(removable_constraints[c]);
747 if (c == removable_constraints.size()) {
759 std::vector<int> removable_constraints;
761 constraint_weights_.reserve(num_constraints);
763 for (
int c = 0; c < num_constraints; ++c) {
765 case ConstraintProto::kCumulative:
766 case ConstraintProto::kAllDiff:
767 case ConstraintProto::kElement:
768 case ConstraintProto::kRoutes:
769 case ConstraintProto::kCircuit:
770 constraint_weights_.push_back(3.0);
771 num_removable_constraints_++;
773 case ConstraintProto::kBoolOr:
774 case ConstraintProto::kBoolAnd:
775 case ConstraintProto::kBoolXor:
776 case ConstraintProto::kIntProd:
777 case ConstraintProto::kIntDiv:
778 case ConstraintProto::kIntMod:
779 case ConstraintProto::kIntMax:
780 case ConstraintProto::kLinMax:
781 case ConstraintProto::kIntMin:
782 case ConstraintProto::kLinMin:
783 case ConstraintProto::kNoOverlap:
784 case ConstraintProto::kNoOverlap2D:
785 constraint_weights_.push_back(2.0);
786 num_removable_constraints_++;
788 case ConstraintProto::kLinear:
789 case ConstraintProto::kTable:
790 case ConstraintProto::kAutomaton:
791 case ConstraintProto::kInverse:
792 case ConstraintProto::kReservoir:
793 case ConstraintProto::kAtMostOne:
794 case ConstraintProto::kExactlyOne:
795 constraint_weights_.push_back(1.0);
796 num_removable_constraints_++;
798 case ConstraintProto::CONSTRAINT_NOT_SET:
799 case ConstraintProto::kInterval:
802 constraint_weights_.push_back(0.0);
808 void WeightedRandomRelaxationNeighborhoodGenerator::
809 AdditionalProcessingOnSynchronize(
const SolveData& solve_data) {
810 const IntegerValue best_objective_improvement =
811 solve_data.new_objective_bound - solve_data.initial_best_objective_bound;
813 const std::vector<int>& removed_constraints =
814 removed_constraints_[solve_data.neighborhood_id];
828 if (best_objective_improvement > 0) {
830 for (
int c : removed_constraints) {
831 if (constraint_weights_[c] <= 90.0) {
832 constraint_weights_[c] += 10.0;
834 constraint_weights_[c] = 100.0;
838 best_objective_improvement < 0) {
840 for (
int c : removed_constraints) {
841 if (constraint_weights_[c] > 0.5) {
842 constraint_weights_[c] -= 0.5;
846 removed_constraints_.erase(solve_data.neighborhood_id);
850 const CpSolverResponse& initial_solution,
double difficulty,
851 absl::BitGenRef random) {
852 const int target_size =
853 std::round((1.0 -
difficulty) * num_removable_constraints_);
855 std::vector<int> removed_constraints;
860 std::vector<std::pair<double, int>> constraint_removal_scores;
861 std::uniform_real_distribution<double> random_var(0.0, 1.0);
862 for (
int c = 0; c < constraint_weights_.size(); ++c) {
863 if (constraint_weights_[c] <= 0)
continue;
864 const double u = random_var(random);
865 const double score = std::pow(u, (1 / constraint_weights_[c]));
866 constraint_removal_scores.push_back({score, c});
868 std::sort(constraint_removal_scores.rbegin(),
869 constraint_removal_scores.rend());
870 for (
int i = 0; i < target_size; ++i) {
871 removed_constraints.push_back(constraint_removal_scores[i].second);
875 absl::MutexLock mutex_lock(&
mutex_);
876 result.
id = next_available_id_;
877 next_available_id_++;
878 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 CHECK_LE(val1, val2)
#define VLOG(verboselevel)
void Update(int num_decreases, int num_increases)
We call domain any subset of Int64 = [kint64min, kint64max].
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
bool IsEmpty() const
Returns true if this is the empty set.
bool Contains(int64 value) const
Returns true iff value is in 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)
const SharedResponseManager & shared_response() const
const std::vector< std::vector< int > > & VarToConstraint() const
Neighborhood FullNeighborhood() const
Neighborhood FixAllVariables(const CpSolverResponse &initial_solution) const
bool IsActive(int var) const
Neighborhood FixGivenVariables(const CpSolverResponse &initial_solution, const std::vector< int > &variables_to_fix) const
const absl::Span< const int > TypeToConstraints(ConstraintProto::ConstraintCase type) const
Neighborhood RelaxGivenVariables(const CpSolverResponse &initial_solution, const std::vector< int > &relaxed_variables) const
const std::vector< int > & ActiveVariables() const
const CpModelProto & ModelProto() const
Neighborhood RemoveMarkedConstraints(const std::vector< int > &constraints_to_remove) const
const std::vector< std::vector< int > > & ConstraintToVar() const
void Synchronize() override
virtual bool IsRelaxationGenerator() const
virtual bool ReadyToGenerate() const
double difficulty() const
double GetUCBScore(int64 total_num_calls) const
virtual void AdditionalProcessingOnSynchronize(const SolveData &solve_data)
const NeighborhoodGeneratorHelper & helper_
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
void GetChangedBounds(int id, std::vector< int > *variables, std::vector< int64 > *new_lower_bounds, std::vector< int64 > *new_upper_bounds)
bool HasNewSolution() const
const SharedSolutionRepository< int64 > & SolutionsRepository() 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
WeightedRandomRelaxationNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, const std::string &name)
Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random) final
CpModelProto const * model_proto
static const int64 kint64min
std::vector< int > UsedVariables(const ConstraintProto &ct)
bool RefIsPositive(int ref)
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)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
int64 CapSub(int64 x, int64 y)
std::vector< std::pair< int, std::pair< int64, int64 > > > reduced_domain_vars
std::vector< std::pair< int, int64 > > fixed_vars
#define VLOG_IS_ON(verboselevel)