20 #include "absl/memory/memory.h"
21 #include "google/protobuf/text_format.h"
35 using ::operations_research::glop::ColIndex;
37 using ::operations_research::glop::LinearProgram;
38 using ::operations_research::glop::LPSolver;
39 using ::operations_research::sat::LinearBooleanConstraint;
40 using ::operations_research::sat::LinearBooleanProblem;
47 void UseBopSolutionForSatAssignmentPreference(
const BopSolution& solution,
48 sat::SatSolver* solver) {
49 for (
int i = 0; i < solution.Size(); ++i) {
50 solver->SetAssignmentPreference(
51 sat::Literal(sat::BooleanVariable(i), solution.Value(VariableIndex(i))),
61 objective_terms_(objective_terms) {}
66 const ProblemState& problem_state,
int num_relaxed_vars) {
67 if (state_update_stamp_ == problem_state.
update_stamp()) {
73 sat_solver_ = absl::make_unique<sat::SatSolver>();
84 std::vector<sat::LiteralWithCoeff> cst;
85 for (BopConstraintTerm term : objective_terms_) {
86 if (problem_state.
solution().
Value(term.var_id) && term.weight < 0) {
88 sat::Literal(sat::BooleanVariable(term.var_id.value()),
false), 1.0));
91 cst.push_back(sat::LiteralWithCoeff(
92 sat::Literal(sat::BooleanVariable(term.var_id.value()),
true), 1.0));
95 sat_solver_->AddLinearConstraint(
96 false, sat::Coefficient(0),
97 true, sat::Coefficient(num_relaxed_vars), &cst);
104 UseBopSolutionForSatAssignmentPreference(problem_state.
solution(),
109 bool BopCompleteLNSOptimizer::ShouldBeRun(
110 const ProblemState& problem_state)
const {
111 return problem_state.solution().IsFeasible();
115 const BopParameters&
parameters,
const ProblemState& problem_state,
116 LearnedInfo* learned_info, TimeLimit*
time_limit) {
118 CHECK(learned_info !=
nullptr);
120 learned_info->Clear();
123 SynchronizeIfNeeded(problem_state,
parameters.num_relaxed_vars());
128 CHECK(sat_solver_ !=
nullptr);
129 const double initial_dt = sat_solver_->deterministic_time();
131 time_limit->AdvanceDeterministicTime(sat_solver_->deterministic_time() -
138 sat::SatParameters sat_params;
139 sat_params.set_max_number_of_conflicts(
140 parameters.max_number_of_conflicts_in_random_lns());
141 sat_params.set_max_time_in_seconds(
time_limit->GetTimeLeft());
142 sat_params.set_max_deterministic_time(
time_limit->GetDeterministicTimeLeft());
143 sat_params.set_random_seed(
parameters.random_seed());
145 sat_solver_->SetParameters(sat_params);
149 &learned_info->solution);
167 bool UseLinearRelaxationForSatAssignmentPreference(
168 const BopParameters&
parameters,
const LinearBooleanProblem& problem,
169 sat::SatSolver* sat_solver, TimeLimit*
time_limit) {
172 glop::LinearProgram lp_model;
176 const sat::Trail& propagation_trail = sat_solver->LiteralTrail();
177 for (
int trail_index = 0; trail_index < propagation_trail.Index();
179 const sat::Literal fixed_literal = propagation_trail[trail_index];
181 lp_model.SetVariableBounds(ColIndex(fixed_literal.Variable().value()),
185 glop::LPSolver lp_solver;
189 lp_solver.SolveWithTimeLimit(lp_model, nested_time_limit.GetTimeLimit());
192 lp_status != glop::ProblemStatus::PRIMAL_FEASIBLE &&
199 for (ColIndex
col(0);
col < lp_solver.variable_values().size(); ++
col) {
200 const double value = lp_solver.variable_values()[
col];
201 sat_solver->SetAssignmentPreference(
202 sat::Literal(sat::BooleanVariable(
col.value()), round(
value) == 1),
214 const std::string&
name,
bool use_lp_to_guide_sat,
218 use_lp_to_guide_sat_(use_lp_to_guide_sat),
219 neighborhood_generator_(neighborhood_generator),
220 sat_propagator_(sat_propagator),
221 adaptive_difficulty_(0.001) {
222 CHECK(sat_propagator !=
nullptr);
227 bool BopAdaptiveLNSOptimizer::ShouldBeRun(
233 const BopParameters&
parameters,
const ProblemState& problem_state,
236 CHECK(learned_info !=
nullptr);
238 learned_info->Clear();
242 auto sat_propagator_cleanup =
245 sat_propagator_->SetAssumptionLevel(0);
246 sat_propagator_->RestoreSolverToAssumptionLevel();
247 ExtractLearnedInfoFromSatSolver(sat_propagator_, learned_info);
261 const BopParameters& local_parameters =
parameters;
264 num_tries < local_parameters.num_random_lns_tries()) {
268 neighborhood_generator_->GenerateNeighborhood(problem_state, difficulty,
272 VLOG(2) << num_tries <<
" difficulty:" << difficulty
273 <<
" luby:" << adaptive_difficulty_.
luby_value()
275 << problem_state.original_problem().num_variables();
280 VLOG(2) <<
"Nothing fixed!";
290 sat::SatParameters params;
291 params.set_max_number_of_conflicts(
292 local_parameters.max_number_of_conflicts_for_quick_check());
293 params.set_max_time_in_seconds(
time_limit->GetTimeLeft());
294 params.set_max_deterministic_time(
time_limit->GetDeterministicTimeLeft());
295 params.set_random_seed(
parameters.random_seed());
304 &learned_info->solution);
325 return problem_state.solution().IsFeasible()
336 const int conflict_limit =
338 parameters.max_number_of_conflicts_in_random_lns();
340 sat::SatParameters params;
341 params.set_max_number_of_conflicts(conflict_limit);
342 params.set_max_time_in_seconds(
time_limit->GetTimeLeft());
343 params.set_max_deterministic_time(
time_limit->GetDeterministicTimeLeft());
344 params.set_random_seed(
parameters.random_seed());
346 sat::SatSolver sat_solver;
347 sat_solver.SetParameters(params);
350 const LinearBooleanProblem& problem = problem_state.original_problem();
351 sat_solver.SetNumVariables(problem.num_variables());
368 if (use_lp_to_guide_sat_) {
369 if (!UseLinearRelaxationForSatAssignmentPreference(
378 problem, sat::Coefficient(problem_state.solution().GetCost()) - 1,
387 time_limit->AdvanceDeterministicTime(sat_solver.deterministic_time());
391 &learned_info->solution);
396 if (sat_solver.num_failures() < 0.5 * conflict_limit) {
398 }
else if (sat_solver.num_failures() > 0.95 * conflict_limit) {
412 std::vector<sat::Literal> ObjectiveVariablesAssignedToTheirLowCostValue(
413 const ProblemState& problem_state,
415 std::vector<sat::Literal> result;
416 DCHECK(problem_state.solution().IsFeasible());
417 for (
const BopConstraintTerm& term : objective_terms) {
418 if (((problem_state.solution().Value(term.var_id) && term.weight < 0) ||
419 (!problem_state.solution().Value(term.var_id) && term.weight > 0))) {
421 sat::Literal(sat::BooleanVariable(term.var_id.value()),
422 problem_state.solution().Value(term.var_id)));
430 void ObjectiveBasedNeighborhood::GenerateNeighborhood(
431 const ProblemState& problem_state,
double difficulty,
432 sat::SatSolver* sat_propagator) {
434 std::vector<sat::Literal> candidates =
435 ObjectiveVariablesAssignedToTheirLowCostValue(problem_state,
437 std::shuffle(candidates.begin(), candidates.end(), *random_);
441 const int num_variables = sat_propagator->NumVariables();
442 const int target = round((1.0 - difficulty) * num_variables);
444 sat_propagator->Backtrack(0);
445 for (
const sat::Literal
literal : candidates) {
446 if (sat_propagator->LiteralTrail().Index() == target)
break;
447 if (sat_propagator->LiteralTrail().Index() > target) {
450 sat_propagator->Backtrack(
451 std::max(0, sat_propagator->CurrentDecisionLevel() - 1));
454 sat_propagator->EnqueueDecisionAndBacktrackOnConflict(
literal);
455 if (sat_propagator->IsModelUnsat())
return;
459 void ConstraintBasedNeighborhood::GenerateNeighborhood(
460 const ProblemState& problem_state,
double difficulty,
461 sat::SatSolver* sat_propagator) {
463 const LinearBooleanProblem& problem = problem_state.original_problem();
464 const int num_constraints = problem.constraints_size();
465 std::vector<int> ct_ids(num_constraints, 0);
466 for (
int ct_id = 0; ct_id < num_constraints; ++ct_id) ct_ids[ct_id] = ct_id;
467 std::shuffle(ct_ids.begin(), ct_ids.end(), *random_);
471 const int num_variables = sat_propagator->NumVariables();
472 const int target = round(difficulty * num_variables);
474 std::vector<bool> variable_is_relaxed(problem.num_variables(),
false);
475 for (
int i = 0; i < ct_ids.size(); ++i) {
476 if (num_relaxed >= target)
break;
477 const LinearBooleanConstraint& constraint = problem.constraints(ct_ids[i]);
481 if (constraint.literals_size() > 0.7 * num_variables)
continue;
483 for (
int j = 0; j < constraint.literals_size(); ++j) {
484 const VariableIndex var_id(constraint.literals(j) - 1);
485 if (!variable_is_relaxed[var_id.value()]) {
487 variable_is_relaxed[var_id.value()] =
true;
497 sat_propagator->Backtrack(0);
498 const std::vector<sat::Literal> to_fix =
499 ObjectiveVariablesAssignedToTheirLowCostValue(problem_state,
501 for (
const sat::Literal
literal : to_fix) {
502 if (variable_is_relaxed[
literal.Variable().value()])
continue;
503 sat_propagator->EnqueueDecisionAndBacktrackOnConflict(
literal);
504 if (sat_propagator->IsModelUnsat())
return;
509 const LinearBooleanProblem& problem,
MTRandom* random)
511 const int num_variables = problem.num_variables();
512 columns_.
resize(num_variables);
521 const double kSizeThreshold = 0.1;
522 for (
int i = 0; i < problem.constraints_size(); ++i) {
523 const LinearBooleanConstraint& constraint = problem.constraints(i);
524 if (constraint.literals_size() > kSizeThreshold * num_variables)
continue;
525 for (
int j = 0; j < constraint.literals_size(); ++j) {
533 void RelationGraphBasedNeighborhood::GenerateNeighborhood(
537 const int num_variables = sat_propagator->
NumVariables();
538 const int target = round(difficulty * num_variables);
540 std::vector<bool> variable_is_relaxed(num_variables,
false);
541 std::deque<int> queue;
546 queue.push_back(random_->
Uniform(num_variables));
547 variable_is_relaxed[queue.back()] =
true;
548 while (!queue.empty() && num_relaxed < target) {
549 const int var = queue.front();
551 for (ConstraintIndex ct_index : columns_[VariableIndex(
var)]) {
552 const LinearBooleanConstraint& constraint =
554 for (
int i = 0; i < constraint.literals_size(); ++i) {
556 const int next_var =
literal.Variable().value();
557 if (!variable_is_relaxed[next_var]) {
559 variable_is_relaxed[next_var] =
true;
560 queue.push_back(next_var);
570 for (sat::BooleanVariable
var(0);
var < num_variables; ++
var) {
573 if (variable_is_relaxed[
literal.Variable().value()])
continue;
578 if (variable_is_relaxed
579 [sat_propagator->
LiteralTrail()[i].Variable().value()]) {
586 VLOG(2) <<
"target:" << target <<
" relaxed:" << num_relaxed <<
" actual:"