28 #include "absl/random/random.h"
29 #include "absl/strings/str_cat.h"
30 #include "absl/strings/str_format.h"
38 #if !defined(__PORTABLE_PLATFORM__) && defined(USE_SCIP)
62 void Log(
const std::string&
message) {
76 std::string CnfObjectiveLine(
const LinearBooleanProblem& problem,
77 Coefficient objective) {
78 const double scaled_objective =
80 return absl::StrFormat(
"o %d",
static_cast<int64_t
>(scaled_objective));
83 struct LiteralWithCoreIndex {
92 template <
typename Vector>
93 void DeleteVectorIndices(
const std::vector<int>& indices, Vector* v) {
95 int indices_index = 0;
96 for (
int i = 0; i < v->size(); ++i) {
97 if (indices_index < indices.size() && i == indices[indices_index]) {
100 (*v)[new_size] = (*v)[i];
136 class FuMalikSymmetryBreaker {
138 FuMalikSymmetryBreaker() {}
141 void StartResolvingNewCore(
int new_core_index) {
142 literal_by_core_.resize(new_core_index);
143 for (
int i = 0; i < new_core_index; ++i) {
144 literal_by_core_[i].clear();
158 std::vector<Literal> ProcessLiteral(
int assumption_index, Literal
b) {
159 if (assumption_index >= info_by_assumption_index_.size()) {
160 info_by_assumption_index_.resize(assumption_index + 1);
167 std::vector<Literal> result;
168 for (LiteralWithCoreIndex data :
169 info_by_assumption_index_[assumption_index]) {
176 result.insert(result.end(), literal_by_core_[data.core_index].begin(),
177 literal_by_core_[data.core_index].end());
181 for (LiteralWithCoreIndex data :
182 info_by_assumption_index_[assumption_index]) {
183 literal_by_core_[data.core_index].push_back(data.literal);
185 info_by_assumption_index_[assumption_index].push_back(
186 LiteralWithCoreIndex(
b, literal_by_core_.size()));
191 void DeleteIndices(
const std::vector<int>& indices) {
192 DeleteVectorIndices(indices, &info_by_assumption_index_);
197 void ClearInfo(
int assumption_index) {
198 CHECK_LE(assumption_index, info_by_assumption_index_.size());
199 info_by_assumption_index_[assumption_index].clear();
203 void AddInfo(
int assumption_index, Literal
b) {
204 CHECK_GE(assumption_index, info_by_assumption_index_.size());
205 info_by_assumption_index_.resize(assumption_index + 1);
206 info_by_assumption_index_[assumption_index].push_back(
207 LiteralWithCoreIndex(
b, literal_by_core_.size()));
211 std::vector<std::vector<LiteralWithCoreIndex>> info_by_assumption_index_;
212 std::vector<std::vector<Literal>> literal_by_core_;
220 std::vector<Literal>* core) {
222 std::set<LiteralIndex> moved_last;
223 std::vector<Literal> candidate(core->begin(), core->end());
234 if (target_level == -1)
break;
252 if (candidate.empty() || solver->
IsModelUnsat())
return;
253 moved_last.insert(candidate.back().Index());
258 if (candidate.size() < core->size()) {
259 VLOG(1) <<
"minimization " << core->size() <<
" -> " << candidate.size();
260 core->assign(candidate.begin(), candidate.end());
270 const LinearBooleanProblem& problem,
272 std::vector<bool>* solution) {
274 FuMalikSymmetryBreaker symmetry;
295 std::vector<std::vector<Literal>> blocking_clauses;
296 std::vector<Literal> assumptions;
299 const LinearObjective& objective = problem.objective();
300 CHECK_GT(objective.coefficients_size(), 0);
301 const Coefficient unique_objective_coeff(std::abs(objective.coefficients(0)));
302 for (
int i = 0; i < objective.literals_size(); ++i) {
303 CHECK_EQ(std::abs(objective.coefficients(i)), unique_objective_coeff)
304 <<
"The basic Fu & Malik algorithm needs constant objective coeffs.";
310 blocking_clauses.push_back(std::vector<Literal>(1, min_literal));
313 assumptions.push_back(min_literal);
317 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
318 assumptions.size(), problem.num_variables(),
319 problem.constraints_size()));
326 for (
int iter = 0;; ++iter) {
332 logger.Log(CnfObjectiveLine(problem, objective));
348 logger.Log(absl::StrFormat(
"c iter:%d core:%u", iter, core.size()));
351 if (core.size() == 1) {
355 std::find(assumptions.begin(), assumptions.end(), core[0]) -
368 std::vector<int> to_delete(1,
index);
369 DeleteVectorIndices(to_delete, &assumptions);
370 DeleteVectorIndices(to_delete, &blocking_clauses);
371 symmetry.DeleteIndices(to_delete);
373 symmetry.StartResolvingNewCore(iter);
377 if (core.size() == 2) {
387 std::vector<LiteralWithCoeff> at_most_one_constraint;
388 std::vector<Literal> at_least_one_constraint;
396 for (
int i = 0; i < core.size(); ++i) {
401 std::find(assumptions.begin() +
index, assumptions.end(), core[i]) -
406 const Literal a(BooleanVariable(old_num_variables + i),
true);
407 Literal b(BooleanVariable(old_num_variables + core.size() + i),
true);
408 if (core.size() == 2) {
409 b =
Literal(BooleanVariable(old_num_variables + 2),
true);
410 if (i == 1)
b =
b.Negated();
425 if (assumptions[
index].Variable() >= problem.num_variables()) {
430 blocking_clauses[
index].push_back(
b);
434 blocking_clauses[
index].push_back(
a);
436 blocking_clauses[
index].pop_back();
440 at_least_one_constraint.push_back(
b);
443 assumptions[
index] =
a.Negated();
449 &at_most_one_constraint);
459 LOG(
INFO) <<
"Infeasible while adding a clause.";
467 const LinearBooleanProblem& problem,
469 std::vector<bool>* solution) {
471 FuMalikSymmetryBreaker symmetry;
475 Coefficient
lower_bound(
static_cast<int64_t
>(problem.objective().offset()));
479 std::vector<Literal> assumptions;
480 std::vector<Coefficient> costs;
481 std::vector<Literal> reference;
484 const LinearObjective& objective = problem.objective();
485 CHECK_GT(objective.coefficients_size(), 0);
486 for (
int i = 0; i < objective.literals_size(); ++i) {
488 const Coefficient coeff(objective.coefficients(i));
494 costs.push_back(coeff);
496 assumptions.push_back(
literal);
497 costs.push_back(-coeff);
501 reference = assumptions;
504 Coefficient stratified_lower_bound =
505 *std::max_element(costs.begin(), costs.end());
508 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
509 assumptions.size(), problem.num_variables(),
510 problem.constraints_size()));
512 for (
int iter = 0;; ++iter) {
520 std::vector<int> to_delete;
521 int num_above_threshold = 0;
522 for (
int i = 0; i < assumptions.size(); ++i) {
523 if (costs[i] > hardening_threshold) {
527 to_delete.push_back(i);
528 ++num_above_threshold;
532 to_delete.push_back(i);
536 if (!to_delete.empty()) {
537 logger.Log(absl::StrFormat(
"c fixed %u assumptions, %d with cost > %d",
538 to_delete.size(), num_above_threshold,
539 hardening_threshold.value()));
540 DeleteVectorIndices(to_delete, &assumptions);
541 DeleteVectorIndices(to_delete, &costs);
542 DeleteVectorIndices(to_delete, &reference);
543 symmetry.DeleteIndices(to_delete);
548 std::vector<Literal> assumptions_subset;
549 for (
int i = 0; i < assumptions.size(); ++i) {
550 if (costs[i] >= stratified_lower_bound) {
551 assumptions_subset.push_back(assumptions[i]);
563 const Coefficient old_lower_bound = stratified_lower_bound;
564 for (Coefficient
cost : costs) {
565 if (
cost < old_lower_bound) {
566 if (stratified_lower_bound == old_lower_bound ||
567 cost > stratified_lower_bound) {
568 stratified_lower_bound =
cost;
575 const Coefficient objective_offset(
576 static_cast<int64_t
>(problem.objective().offset()));
579 logger.Log(CnfObjectiveLine(problem, objective));
583 if (stratified_lower_bound < old_lower_bound)
continue;
603 for (
int i = 0; i < core.size(); ++i) {
605 std::find(assumptions.begin() +
index, assumptions.end(), core[i]) -
614 logger.Log(absl::StrFormat(
615 "c iter:%d core:%u lb:%d min_cost:%d strat:%d", iter, core.size(),
616 lower_bound.value(), min_cost.value(), stratified_lower_bound.value()));
622 if (min_cost > stratified_lower_bound) {
623 stratified_lower_bound = min_cost;
627 if (core.size() == 1) {
631 std::find(assumptions.begin(), assumptions.end(), core[0]) -
641 std::vector<int> to_delete(1,
index);
642 DeleteVectorIndices(to_delete, &assumptions);
643 DeleteVectorIndices(to_delete, &costs);
644 DeleteVectorIndices(to_delete, &reference);
645 symmetry.DeleteIndices(to_delete);
647 symmetry.StartResolvingNewCore(iter);
651 if (core.size() == 2) {
661 std::vector<LiteralWithCoeff> at_most_one_constraint;
662 std::vector<Literal> at_least_one_constraint;
670 for (
int i = 0; i < core.size(); ++i) {
675 std::find(assumptions.begin() +
index, assumptions.end(), core[i]) -
680 const Literal a(BooleanVariable(old_num_variables + i),
true);
681 Literal b(BooleanVariable(old_num_variables + core.size() + i),
true);
682 if (core.size() == 2) {
683 b =
Literal(BooleanVariable(old_num_variables + 2),
true);
684 if (i == 1)
b =
b.Negated();
706 if (costs[
index] == min_cost) {
708 assumptions[
index] =
a.Negated();
718 symmetry.AddInfo(assumptions.size(),
b);
719 symmetry.ClearInfo(
index);
722 costs[
index] -= min_cost;
730 assumptions.push_back(
a.Negated());
731 costs.push_back(min_cost);
732 reference.push_back(reference[
index]);
744 at_least_one_constraint.push_back(reference[
index].Negated());
750 &at_most_one_constraint);
756 LOG(
INFO) <<
"Unsat while adding a clause.";
764 const LinearBooleanProblem& problem,
766 std::vector<bool>* solution) {
768 const SatParameters initial_parameters = solver->
parameters();
771 SatParameters
parameters = initial_parameters;
776 int max_number_of_conflicts = 5;
781 Coefficient best(min_seen);
782 for (
int i = 0; i < num_times; ++i) {
786 parameters.set_max_number_of_conflicts(max_number_of_conflicts);
792 const bool use_obj = absl::Bernoulli(random, 1.0 / 4);
811 std::vector<bool> candidate;
815 if (objective < best) {
816 *solution = candidate;
818 logger.Log(CnfObjectiveLine(problem, objective));
823 objective - 1, solver)) {
827 min_seen =
std::min(min_seen, objective);
828 max_seen =
std::max(max_seen, objective);
830 logger.Log(absl::StrCat(
831 "c ", objective.value(),
" [", min_seen.value(),
", ", max_seen.value(),
832 "] objective_preference: ", use_obj ?
"true" :
"false",
" ",
844 const LinearBooleanProblem& problem,
846 std::vector<bool>* solution) {
853 if (!solution->empty()) {
862 objective - 1, solver)) {
882 const Coefficient old_objective = objective;
885 logger.Log(CnfObjectiveLine(problem, objective));
891 std::vector<bool>* solution) {
893 std::deque<EncodingNode> repository;
896 Coefficient offset(0);
897 std::vector<EncodingNode*>
nodes =
902 const Coefficient reference =
nodes.front()->weight();
908 if (!solution->empty()) {
915 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
916 nodes.size(), problem.num_variables(),
917 problem.constraints_size()));
923 logger.Log(absl::StrFormat(
"c encoding depth:%d", root->
depth()));
929 const int index = offset.value() + objective.value();
950 const Coefficient old_objective = objective;
953 logger.Log(CnfObjectiveLine(problem, objective));
959 std::vector<bool>* solution) {
964 Coefficient offset(0);
965 std::deque<EncodingNode> repository;
966 std::vector<EncodingNode*>
nodes =
973 if (!solution->empty()) {
979 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
980 nodes.size(), problem.num_variables(),
981 problem.constraints_size()));
984 Coefficient stratified_lower_bound(0);
986 SatParameters::STRATIFICATION_DESCENT) {
989 stratified_lower_bound =
std::max(stratified_lower_bound, n->weight());
995 std::string previous_core_info =
"";
996 for (
int iter = 0;; ++iter) {
1002 const std::string gap_string =
1007 absl::StrFormat(
"c iter:%d [%s] lb:%d%s assumptions:%u depth:%d", iter,
1010 static_cast<int64_t
>(problem.objective().offset()),
1011 gap_string,
nodes.size(), max_depth));
1018 std::vector<bool> temp_solution;
1023 *solution = temp_solution;
1024 logger.Log(CnfObjectiveLine(problem, obj));
1030 stratified_lower_bound =
1032 if (stratified_lower_bound > 0)
continue;
1044 previous_core_info =
1045 absl::StrFormat(
"core:%u mw:%d", core.size(), min_weight.value());
1048 if (stratified_lower_bound < min_weight &&
1050 SatParameters::STRATIFICATION_ASCENT) {
1051 stratified_lower_bound = min_weight;
1060 IntegerVariable objective_var,
1061 const std::function<
void()>& feasible_solution_observer,
Model*
model) {
1064 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1072 const IntegerValue objective = integer_trail->LowerBound(objective_var);
1075 if (feasible_solution_observer !=
nullptr) {
1076 feasible_solution_observer();
1078 if (
parameters.stop_after_first_solution()) {
1084 if (!integer_trail->Enqueue(
1093 IntegerVariable objective_var,
1094 const std::function<
void()>& feasible_solution_observer,
Model*
model) {
1095 const SatParameters old_params = *
model->GetOrCreate<SatParameters>();
1102 SatParameters new_params = old_params;
1103 new_params.set_max_number_of_conflicts(
1104 old_params.binary_search_num_conflicts());
1105 *
model->GetOrCreate<SatParameters>() = new_params;
1111 IntegerValue unknown_min = integer_trail->UpperBound(objective_var);
1112 IntegerValue unknown_max = integer_trail->LowerBound(objective_var);
1114 sat_solver->Backtrack(0);
1115 const IntegerValue lb = integer_trail->LowerBound(objective_var);
1116 const IntegerValue ub = integer_trail->UpperBound(objective_var);
1117 unknown_min =
std::min(unknown_min, ub);
1118 unknown_max =
std::max(unknown_max, lb);
1121 IntegerValue target;
1122 if (lb < unknown_min) {
1123 target = lb + (unknown_min - lb) / 2;
1124 }
else if (unknown_max < ub) {
1125 target = ub - (ub - unknown_max) / 2;
1127 VLOG(1) <<
"Binary-search, done.";
1130 VLOG(1) <<
"Binary-search, objective: [" << lb <<
"," << ub <<
"]"
1131 <<
" tried: [" << unknown_min <<
"," << unknown_max <<
"]"
1132 <<
" target: obj<=" << target;
1135 const Literal assumption = integer_encoder->GetOrCreateAssociatedLiteral(
1149 sat_solver->Backtrack(0);
1150 if (!integer_trail->Enqueue(
1159 const IntegerValue objective = integer_trail->LowerBound(objective_var);
1160 if (feasible_solution_observer !=
nullptr) {
1161 feasible_solution_observer();
1166 sat_solver->Backtrack(0);
1167 if (!integer_trail->Enqueue(
1175 unknown_min =
std::min(target, unknown_min);
1176 unknown_max =
std::max(target, unknown_max);
1182 sat_solver->Backtrack(0);
1183 *
model->GetOrCreate<SatParameters>() = old_params;
1210 std::vector<IntegerValue> assumption_weights,
1211 IntegerValue stratified_threshold, Model*
model,
1212 std::vector<std::vector<Literal>>* cores) {
1214 SatSolver* sat_solver =
model->GetOrCreate<SatSolver>();
1222 std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
1223 if (sat_solver->parameters().minimize_core()) {
1226 CHECK(!core.empty());
1227 cores->push_back(core);
1228 if (!sat_solver->parameters().find_multiple_cores())
break;
1232 std::vector<int> indices;
1234 std::set<Literal> temp(core.begin(), core.end());
1235 for (
int i = 0; i < assumptions.size(); ++i) {
1237 indices.push_back(i);
1247 IntegerValue min_weight = assumption_weights[indices.front()];
1248 for (
const int i : indices) {
1249 min_weight =
std::min(min_weight, assumption_weights[i]);
1251 for (
const int i : indices) {
1252 assumption_weights[i] -= min_weight;
1258 for (
int i = 0; i < assumptions.size(); ++i) {
1259 if (assumption_weights[i] < stratified_threshold)
continue;
1260 assumptions[new_size] = assumptions[i];
1261 assumption_weights[new_size] = assumption_weights[i];
1264 assumptions.resize(new_size);
1265 assumption_weights.resize(new_size);
1266 }
while (!assumptions.empty());
1273 std::vector<Literal> assumptions, Model*
model,
1274 std::vector<std::vector<Literal>>* cores) {
1276 SatSolver* sat_solver =
model->GetOrCreate<SatSolver>();
1277 TimeLimit* limit =
model->GetOrCreate<TimeLimit>();
1284 std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
1285 if (sat_solver->parameters().minimize_core()) {
1288 CHECK(!core.empty());
1289 cores->push_back(core);
1290 if (!sat_solver->parameters().find_multiple_cores())
break;
1294 CHECK(!core.empty());
1295 auto* random =
model->GetOrCreate<ModelRandomGenerator>();
1296 const Literal random_literal =
1297 core[absl::Uniform<int>(*random, 0, core.size())];
1298 for (
int i = 0; i < assumptions.size(); ++i) {
1299 if (assumptions[i] == random_literal) {
1300 std::swap(assumptions[i], assumptions.back());
1301 assumptions.pop_back();
1305 }
while (!assumptions.empty());
1312 IntegerVariable objective_var,
1313 const std::vector<IntegerVariable>& variables,
1315 std::function<
void()> feasible_solution_observer,
Model*
model)
1316 : parameters_(
model->GetOrCreate<SatParameters>()),
1322 objective_var_(objective_var),
1323 feasible_solution_observer_(std::move(feasible_solution_observer)) {
1325 for (
int i = 0; i < variables.size(); ++i) {
1333 terms_.back().depth = 0;
1339 stratification_threshold_ = parameters_->max_sat_stratification() ==
1340 SatParameters::STRATIFICATION_NONE
1345 bool CoreBasedOptimizer::ProcessSolution() {
1348 IntegerValue objective(0);
1349 for (ObjectiveTerm& term : terms_) {
1351 objective += term.weight *
value;
1366 if (feasible_solution_observer_ !=
nullptr) {
1367 feasible_solution_observer_();
1369 if (parameters_->stop_after_first_solution()) {
1377 return integer_trail_->
Enqueue(
1381 bool CoreBasedOptimizer::PropagateObjectiveBounds() {
1383 bool some_bound_were_tightened =
true;
1384 while (some_bound_were_tightened) {
1385 some_bound_were_tightened =
false;
1389 IntegerValue implied_objective_lb(0);
1390 for (ObjectiveTerm& term : terms_) {
1391 const IntegerValue var_lb = integer_trail_->
LowerBound(term.var);
1392 term.old_var_lb = var_lb;
1393 implied_objective_lb += term.weight * var_lb.value();
1397 if (implied_objective_lb > integer_trail_->
LowerBound(objective_var_)) {
1399 objective_var_, implied_objective_lb),
1404 some_bound_were_tightened =
true;
1413 const IntegerValue gap =
1414 integer_trail_->
UpperBound(objective_var_) - implied_objective_lb;
1416 for (
const ObjectiveTerm& term : terms_) {
1417 if (term.weight == 0)
continue;
1418 const IntegerValue var_lb = integer_trail_->
LowerBound(term.var);
1419 const IntegerValue var_ub = integer_trail_->
UpperBound(term.var);
1420 if (var_lb == var_ub)
continue;
1427 if (gap / term.weight < var_ub - var_lb) {
1428 some_bound_were_tightened =
true;
1429 const IntegerValue new_ub = var_lb + gap / term.weight;
1451 void CoreBasedOptimizer::ComputeNextStratificationThreshold() {
1452 std::vector<IntegerValue> weights;
1453 for (ObjectiveTerm& term : terms_) {
1454 if (term.weight >= stratification_threshold_)
continue;
1455 if (term.weight == 0)
continue;
1459 if (var_lb == var_ub)
continue;
1461 weights.push_back(term.weight);
1463 if (weights.empty()) {
1464 stratification_threshold_ = IntegerValue(0);
1469 stratification_threshold_ =
1470 weights[
static_cast<int>(std::floor(0.9 * weights.size()))];
1473 bool CoreBasedOptimizer::CoverOptimization() {
1476 constexpr
double max_dtime_per_core = 0.5;
1477 const double old_time_limit = parameters_->max_deterministic_time();
1478 parameters_->set_max_deterministic_time(max_dtime_per_core);
1480 parameters_->set_max_deterministic_time(old_time_limit);
1483 for (
const ObjectiveTerm& term : terms_) {
1487 if (term.depth == 0)
continue;
1493 const IntegerVariable
var = term.var;
1504 const double deterministic_limit =
1516 VLOG(1) <<
"cover_opt var:" <<
var <<
" domain:["
1518 if (!ProcessSolution())
return false;
1537 if (!PropagateObjectiveBounds())
return false;
1546 std::map<LiteralIndex, int> literal_to_term_index;
1559 if (parameters_->cover_optimization()) {
1565 std::vector<int> term_indices;
1566 std::vector<IntegerLiteral> integer_assumptions;
1567 std::vector<IntegerValue> assumption_weights;
1568 IntegerValue objective_offset(0);
1569 bool some_assumptions_were_skipped =
false;
1570 for (
int i = 0; i < terms_.size(); ++i) {
1571 const ObjectiveTerm term = terms_[i];
1574 if (term.weight == 0)
continue;
1580 const IntegerValue var_lb = integer_trail_->
LowerBound(term.var);
1581 const IntegerValue var_ub = integer_trail_->
UpperBound(term.var);
1582 if (var_lb == var_ub) {
1583 objective_offset += term.weight * var_lb.value();
1588 if (term.weight >= stratification_threshold_) {
1589 integer_assumptions.push_back(
1591 assumption_weights.push_back(term.weight);
1592 term_indices.push_back(i);
1594 some_assumptions_were_skipped =
true;
1599 if (term_indices.empty() && some_assumptions_were_skipped) {
1600 ComputeNextStratificationThreshold();
1605 if (term_indices.size() <= 2 && !some_assumptions_were_skipped) {
1606 VLOG(1) <<
"Switching to linear scan...";
1607 if (!already_switched_to_linear_scan_) {
1608 already_switched_to_linear_scan_ =
true;
1609 std::vector<IntegerVariable> constraint_vars;
1610 std::vector<int64_t> constraint_coeffs;
1611 for (
const int index : term_indices) {
1612 constraint_vars.push_back(terms_[
index].
var);
1613 constraint_coeffs.push_back(terms_[
index].
weight.value());
1615 constraint_vars.push_back(objective_var_);
1616 constraint_coeffs.push_back(-1);
1618 -objective_offset.value()));
1622 objective_var_, feasible_solution_observer_, model_);
1628 for (
const ObjectiveTerm& term : terms_) {
1629 max_depth =
std::max(max_depth, term.depth);
1631 const int64_t lb = integer_trail_->
LowerBound(objective_var_).value();
1632 const int64_t ub = integer_trail_->
UpperBound(objective_var_).value();
1636 :
static_cast<int>(std::ceil(
1637 100.0 * (ub - lb) /
std::max(std::abs(ub), std::abs(lb))));
1638 VLOG(1) << absl::StrCat(
"unscaled_next_obj_range:[", lb,
",", ub,
1641 gap,
"%",
" assumptions:", term_indices.size(),
1642 " strat:", stratification_threshold_.value(),
1643 " depth:", max_depth);
1647 std::vector<Literal> assumptions;
1648 literal_to_term_index.clear();
1649 for (
int i = 0; i < integer_assumptions.size(); ++i) {
1651 integer_assumptions[i]));
1659 literal_to_term_index[assumptions.back().Index()] = term_indices[i];
1667 std::vector<std::vector<Literal>> cores;
1669 FindCores(assumptions, assumption_weights, stratification_threshold_,
1675 if (cores.empty()) {
1676 ComputeNextStratificationThreshold();
1685 for (
const std::vector<Literal>& core : cores) {
1688 if (core.size() == 1)
continue;
1693 bool ignore_this_core =
false;
1695 IntegerValue max_weight(0);
1696 IntegerValue new_var_lb(1);
1697 IntegerValue new_var_ub(0);
1699 for (
const Literal lit : core) {
1704 if (terms_[
index].old_var_lb <
1706 ignore_this_core =
true;
1717 if (ignore_this_core)
continue;
1719 VLOG(1) << absl::StrFormat(
1720 "core:%u weight:[%d,%d] domain:[%d,%d] depth:%d", core.size(),
1721 min_weight.value(), max_weight.value(), new_var_lb.value(),
1722 new_var_ub.value(), new_depth);
1726 const IntegerVariable new_var =
1728 terms_.push_back({new_var, min_weight, new_depth});
1729 terms_.back().cover_ub = new_var_ub;
1733 std::vector<IntegerVariable> constraint_vars;
1734 std::vector<int64_t> constraint_coeffs;
1735 for (
const Literal lit : core) {
1737 terms_[
index].weight -= min_weight;
1738 constraint_vars.push_back(terms_[
index].
var);
1739 constraint_coeffs.push_back(1);
1741 constraint_vars.push_back(new_var);
1742 constraint_coeffs.push_back(-1);
1760 const std::function<
void()>& feasible_solution_observer,
Model*
model) {
1761 #if !defined(__PORTABLE_PLATFORM__) && defined(USE_SCIP)
1763 IntegerVariable objective_var = objective_definition.
objective_var;
1764 std::vector<IntegerVariable> variables = objective_definition.
vars;
1772 const auto process_solution = [&]() {
1775 IntegerValue objective(0);
1776 for (
int i = 0; i < variables.size(); ++i) {
1780 if (objective > integer_trail->UpperBound(objective_var))
return true;
1782 if (feasible_solution_observer !=
nullptr) {
1783 feasible_solution_observer();
1790 if (!integer_trail->Enqueue(
1800 MPModelRequest request;
1801 request.set_solver_specific_parameters(
"limits/gap = 0");
1802 request.set_solver_type(MPModelRequest::SCIP_MIXED_INTEGER_PROGRAMMING);
1804 MPModelProto& hs_model = *request.mutable_model();
1805 const int num_variables_in_objective = variables.size();
1806 for (
int i = 0; i < num_variables_in_objective; ++i) {
1811 const IntegerVariable
var = variables[i];
1812 MPVariableProto* var_proto = hs_model.add_variable();
1813 var_proto->set_lower_bound(integer_trail->LowerBound(
var).value());
1814 var_proto->set_upper_bound(integer_trail->UpperBound(
var).value());
1816 var_proto->set_is_integer(
true);
1829 std::map<LiteralIndex, std::vector<int>> assumption_to_indices;
1832 std::map<std::pair<int, double>,
int> created_var;
1834 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1838 for (
int iter = 0;; ++iter) {
1846 const IntegerValue mip_objective(
1847 static_cast<int64_t
>(std::round(
response.objective_value())));
1848 VLOG(1) <<
"constraints: " << hs_model.constraint_size()
1849 <<
" variables: " << hs_model.variable_size() <<
" hs_lower_bound: "
1851 <<
" strat: " << stratified_threshold;
1857 if (!integer_trail->Enqueue(
1866 std::vector<Literal> assumptions;
1867 assumption_to_indices.clear();
1868 IntegerValue next_stratified_threshold(0);
1869 for (
int i = 0; i < num_variables_in_objective; ++i) {
1870 const IntegerValue hs_value(
1871 static_cast<int64_t
>(
response.variable_value(i)));
1872 if (hs_value == integer_trail->UpperBound(variables[i]))
continue;
1876 next_stratified_threshold =
1881 assumptions.push_back(integer_encoder->GetOrCreateAssociatedLiteral(
1883 assumption_to_indices[assumptions.back().Index()].push_back(i);
1888 if (assumptions.empty() && next_stratified_threshold > 0) {
1889 CHECK_LT(next_stratified_threshold, stratified_threshold);
1890 stratified_threshold = next_stratified_threshold;
1899 std::vector<std::vector<Literal>> cores;
1900 result = FindMultipleCoresForMaxHs(assumptions,
model, &cores);
1903 if (
parameters.stop_after_first_solution()) {
1906 if (cores.empty()) {
1909 stratified_threshold = next_stratified_threshold;
1910 if (stratified_threshold == 0)
break;
1920 for (
const std::vector<Literal>& core : cores) {
1921 if (core.size() == 1) {
1922 for (
const int index :
1924 hs_model.mutable_variable(
index)->set_lower_bound(
1925 integer_trail->LowerBound(variables[
index]).value());
1931 MPConstraintProto*
ct = hs_model.add_constraint();
1932 ct->set_lower_bound(1.0);
1933 for (
const Literal lit : core) {
1934 for (
const int index :
1936 const double lb = integer_trail->LowerBound(variables[
index]).value();
1938 if (hs_value == lb) {
1940 ct->add_coefficient(1.0);
1941 ct->set_lower_bound(
ct->lower_bound() + lb);
1943 const std::pair<int, double> key = {
index, hs_value};
1945 const int new_var_index = hs_model.variable_size();
1946 created_var[key] = new_var_index;
1948 MPVariableProto* new_var = hs_model.add_variable();
1949 new_var->set_lower_bound(0);
1950 new_var->set_upper_bound(1);
1951 new_var->set_is_integer(
true);
1955 MPConstraintProto* implication = hs_model.add_constraint();
1956 implication->set_lower_bound(lb);
1957 implication->add_var_index(
index);
1958 implication->add_coefficient(1.0);
1959 implication->add_var_index(new_var_index);
1960 implication->add_coefficient(lb - hs_value - 1);
1963 ct->add_coefficient(1.0);
#define CHECK_LT(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define CHECK_GT(val1, val2)
#define CHECK_NE(val1, val2)
#define DCHECK(condition)
#define CHECK_LE(val1, val2)
#define VLOG(verboselevel)
static void SolveWithProto(const MPModelRequest &model_request, MPSolutionResponse *response)
Solves the model encoded by a MPModelRequest protocol buffer and fills the solution encoded as a MPSo...
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
double GetElapsedDeterministicTime() const
Returns the elapsed deterministic time since the construction of this object.
CoreBasedOptimizer(IntegerVariable objective_var, const std::vector< IntegerVariable > &variables, const std::vector< IntegerValue > &coefficients, std::function< void()> feasible_solution_observer, Model *model)
SatSolver::Status Optimize()
Literal literal(int i) const
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
bool IsCurrentlyIgnored(IntegerVariable i) const
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerVariable AddIntegerVariable(IntegerValue lower_bound, IntegerValue upper_bound)
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
IntegerValue LowerBound(IntegerVariable i) const
Class that owns everything related to a particular optimization model.
T Add(std::function< T(Model *)> f)
This makes it possible to have a nicer API on the client side, and it allows both of these forms:
bool AddLinearConstraint(bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, std::vector< LiteralWithCoeff > *cst)
void SetNumVariables(int num_variables)
bool AddTernaryClause(Literal a, Literal b, Literal c)
const SatParameters & parameters() const
void ResetDecisionHeuristic()
Status ResetAndSolveWithGivenAssumptions(const std::vector< Literal > &assumptions)
void SetAssumptionLevel(int assumption_level)
const VariablesAssignment & Assignment() const
int EnqueueDecisionAndBackjumpOnConflict(Literal true_literal)
void SetParameters(const SatParameters ¶meters)
bool AddBinaryClause(Literal a, Literal b)
int EnqueueDecisionAndBacktrackOnConflict(Literal true_literal)
void Backtrack(int target_level)
std::vector< Literal > GetLastIncompatibleDecisions()
bool IsModelUnsat() const
int CurrentDecisionLevel() const
bool AddProblemClause(absl::Span< const Literal > literals)
bool AddUnitClause(Literal true_literal)
bool LiteralIsTrue(Literal literal) const
bool LiteralIsFalse(Literal literal) const
SharedResponseManager * response
SharedTimeLimit * time_limit
absl::Span< const double > coefficients
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
bool ContainsKey(const Collection &collection, const Key &key)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
const Collection::value_type::second_type & FindOrDieNoPrint(const Collection &collection, const typename Collection::value_type::first_type &key)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
bool AddObjectiveConstraint(const LinearBooleanProblem &problem, bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, SatSolver *solver)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
void RestrictObjectiveDomainWithBinarySearch(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
double AddOffsetAndScaleObjectiveValue(const LinearBooleanProblem &problem, Coefficient v)
SatSolver::Status ResetAndSolveIntegerProblem(const std::vector< Literal > &assumptions, Model *model)
SatSolver::Status SolveWithCardinalityEncodingAndCore(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
Coefficient ComputeCoreMinWeight(const std::vector< EncodingNode * > &nodes, const std::vector< Literal > &core)
EncodingNode * MergeAllNodesWithDeque(Coefficient upper_bound, const std::vector< EncodingNode * > &nodes, SatSolver *solver, std::deque< EncodingNode > *repository)
int MoveOneUnprocessedLiteralLast(const std::set< LiteralIndex > &processed, int relevant_prefix_size, std::vector< Literal > *literals)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
std::vector< Literal > ReduceNodesAndExtractAssumptions(Coefficient upper_bound, Coefficient stratified_lower_bound, Coefficient *lower_bound, std::vector< EncodingNode * > *nodes, SatSolver *solver)
void UseObjectiveForSatAssignmentPreference(const LinearBooleanProblem &problem, SatSolver *solver)
SatSolver::Status SolveWithLinearScan(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
void MinimizeCore(SatSolver *solver, std::vector< Literal > *core)
SatSolver::Status MinimizeWithHittingSetAndLazyEncoding(const ObjectiveDefinition &objective_definition, const std::function< void()> &feasible_solution_observer, Model *model)
SatSolver::Status SolveIntegerProblem(Model *model)
std::function< void(Model *)> WeightedSumLowerOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
SatSolver::Status SolveWithWPM1(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
bool IsAssignmentValid(const LinearBooleanProblem &problem, const std::vector< bool > &assignment)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
void MinimizeCoreWithPropagation(TimeLimit *limit, SatSolver *solver, std::vector< Literal > *core)
void ProcessCore(const std::vector< Literal > &core, Coefficient min_weight, std::deque< EncodingNode > *repository, std::vector< EncodingNode * > *nodes, SatSolver *solver)
Coefficient ComputeObjectiveValue(const LinearBooleanProblem &problem, const std::vector< bool > &assignment)
SatSolver::Status SolveWithFuMalik(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
Coefficient MaxNodeWeightSmallerThan(const std::vector< EncodingNode * > &nodes, Coefficient upper_bound)
SatSolver::Status SolveWithRandomParameters(LogBehavior log, const LinearBooleanProblem &problem, int num_times, SatSolver *solver, std::vector< bool > *solution)
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
SatSolver::Status SolveWithCardinalityEncoding(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
void ExtractAssignment(const LinearBooleanProblem &problem, const SatSolver &solver, std::vector< bool > *assignment)
std::vector< EncodingNode * > CreateInitialEncodingNodes(const std::vector< Literal > &literals, const std::vector< Coefficient > &coeffs, Coefficient *offset, std::deque< EncodingNode > *repository)
void RandomizeDecisionHeuristic(URBG *random, SatParameters *parameters)
const Coefficient kCoefficientMax(std::numeric_limits< Coefficient::ValueType >::max())
SatSolver::Status MinimizeIntegerVariableWithLinearScanAndLazyEncoding(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
Collection of objects used to extend the Constraint Solver library.
std::string ProtobufShortDebugString(const P &message)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
std::vector< IntegerValue > coeffs
std::vector< IntegerVariable > vars
IntegerVariable objective_var
double ScaleIntegerObjective(IntegerValue value) const
#define VLOG_IS_ON(verboselevel)