28 #include "absl/random/bit_gen_ref.h" 29 #include "absl/random/random.h" 30 #include "absl/strings/str_cat.h" 31 #include "absl/strings/str_format.h" 39 #if !defined(__PORTABLE_PLATFORM__) && defined(USE_SCIP) 42 #endif // __PORTABLE_PLATFORM__ 62 void Log(
const std::string&
message) {
76 std::string CnfObjectiveLine(
const LinearBooleanProblem& problem,
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());
235 if (target_level == -1)
break;
253 if (candidate.empty() || solver->
IsModelUnsat())
return;
254 moved_last.insert(candidate.back().Index());
259 if (candidate.size() < core->size()) {
260 VLOG(1) <<
"minimization " << core->size() <<
" -> " << candidate.size();
261 core->assign(candidate.begin(), candidate.end());
273 std::vector<bool>* solution) {
275 FuMalikSymmetryBreaker symmetry;
296 std::vector<std::vector<Literal>> blocking_clauses;
297 std::vector<Literal> assumptions;
305 <<
"The basic Fu & Malik algorithm needs constant objective coeffs.";
311 blocking_clauses.push_back(std::vector<Literal>(1, min_literal));
314 assumptions.push_back(min_literal);
318 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
327 for (
int iter = 0;; ++iter) {
333 logger.Log(CnfObjectiveLine(problem, objective));
349 logger.Log(absl::StrFormat(
"c iter:%d core:%u", iter, core.size()));
352 if (core.size() == 1) {
356 std::find(assumptions.begin(), assumptions.end(), core[0]) -
369 std::vector<int> to_delete(1,
index);
370 DeleteVectorIndices(to_delete, &assumptions);
371 DeleteVectorIndices(to_delete, &blocking_clauses);
372 symmetry.DeleteIndices(to_delete);
374 symmetry.StartResolvingNewCore(iter);
378 if (core.size() == 2) {
388 std::vector<LiteralWithCoeff> at_most_one_constraint;
389 std::vector<Literal> at_least_one_constraint;
397 for (
int i = 0; i < core.size(); ++i) {
402 std::find(assumptions.begin() +
index, assumptions.end(), core[i]) -
407 const Literal a(BooleanVariable(old_num_variables + i),
true);
408 Literal b(BooleanVariable(old_num_variables + core.size() + i),
true);
409 if (core.size() == 2) {
410 b =
Literal(BooleanVariable(old_num_variables + 2),
true);
411 if (i == 1)
b =
b.Negated();
431 blocking_clauses[
index].push_back(
b);
435 blocking_clauses[
index].push_back(
a);
437 blocking_clauses[
index].pop_back();
441 at_least_one_constraint.push_back(
b);
444 assumptions[
index] =
a.Negated();
450 &at_most_one_constraint);
460 LOG(
INFO) <<
"Infeasible while adding a clause.";
470 std::vector<bool>* solution) {
472 FuMalikSymmetryBreaker symmetry;
480 std::vector<Literal> assumptions;
481 std::vector<Coefficient> costs;
482 std::vector<Literal> reference;
495 costs.push_back(coeff);
497 assumptions.push_back(
literal);
498 costs.push_back(-coeff);
502 reference = assumptions;
506 *std::max_element(costs.begin(), costs.end());
509 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
513 for (
int iter = 0;; ++iter) {
521 std::vector<int> to_delete;
522 int num_above_threshold = 0;
523 for (
int i = 0; i < assumptions.size(); ++i) {
524 if (costs[i] > hardening_threshold) {
528 to_delete.push_back(i);
529 ++num_above_threshold;
533 to_delete.push_back(i);
537 if (!to_delete.empty()) {
538 logger.Log(absl::StrFormat(
"c fixed %u assumptions, %d with cost > %d",
539 to_delete.size(), num_above_threshold,
540 hardening_threshold.value()));
541 DeleteVectorIndices(to_delete, &assumptions);
542 DeleteVectorIndices(to_delete, &costs);
543 DeleteVectorIndices(to_delete, &reference);
544 symmetry.DeleteIndices(to_delete);
549 std::vector<Literal> assumptions_subset;
550 for (
int i = 0; i < assumptions.size(); ++i) {
551 if (costs[i] >= stratified_lower_bound) {
552 assumptions_subset.push_back(assumptions[i]);
564 const Coefficient old_lower_bound = stratified_lower_bound;
566 if (
cost < old_lower_bound) {
567 if (stratified_lower_bound == old_lower_bound ||
568 cost > stratified_lower_bound) {
569 stratified_lower_bound =
cost;
580 logger.Log(CnfObjectiveLine(problem, objective));
584 if (stratified_lower_bound < old_lower_bound)
continue;
604 for (
int i = 0; i < core.size(); ++i) {
606 std::find(assumptions.begin() +
index, assumptions.end(), core[i]) -
615 logger.Log(absl::StrFormat(
616 "c iter:%d core:%u lb:%d min_cost:%d strat:%d", iter, core.size(),
617 lower_bound.value(), min_cost.value(), stratified_lower_bound.value()));
623 if (min_cost > stratified_lower_bound) {
624 stratified_lower_bound = min_cost;
628 if (core.size() == 1) {
632 std::find(assumptions.begin(), assumptions.end(), core[0]) -
642 std::vector<int> to_delete(1,
index);
643 DeleteVectorIndices(to_delete, &assumptions);
644 DeleteVectorIndices(to_delete, &costs);
645 DeleteVectorIndices(to_delete, &reference);
646 symmetry.DeleteIndices(to_delete);
648 symmetry.StartResolvingNewCore(iter);
652 if (core.size() == 2) {
662 std::vector<LiteralWithCoeff> at_most_one_constraint;
663 std::vector<Literal> at_least_one_constraint;
671 for (
int i = 0; i < core.size(); ++i) {
676 std::find(assumptions.begin() +
index, assumptions.end(), core[i]) -
681 const Literal a(BooleanVariable(old_num_variables + i),
true);
682 Literal b(BooleanVariable(old_num_variables + core.size() + i),
true);
683 if (core.size() == 2) {
684 b =
Literal(BooleanVariable(old_num_variables + 2),
true);
685 if (i == 1)
b =
b.Negated();
707 if (costs[
index] == min_cost) {
709 assumptions[
index] =
a.Negated();
719 symmetry.AddInfo(assumptions.size(),
b);
720 symmetry.ClearInfo(
index);
723 costs[
index] -= min_cost;
731 assumptions.push_back(
a.Negated());
732 costs.push_back(min_cost);
733 reference.push_back(reference[
index]);
745 at_least_one_constraint.push_back(reference[
index].Negated());
751 &at_most_one_constraint);
757 LOG(
INFO) <<
"Unsat while adding a clause.";
766 absl::BitGenRef random,
SatSolver* solver, std::vector<bool>* solution) {
775 int max_number_of_conflicts = 5;
781 for (
int i = 0; i < num_times; ++i) {
791 const bool use_obj = absl::Bernoulli(random, 1.0 / 4);
810 std::vector<bool> candidate;
814 if (objective < best) {
815 *solution = candidate;
817 logger.Log(CnfObjectiveLine(problem, objective));
822 objective - 1, solver)) {
826 min_seen =
std::min(min_seen, objective);
827 max_seen =
std::max(max_seen, objective);
829 logger.Log(absl::StrCat(
830 "c ", objective.value(),
" [", min_seen.value(),
", ", max_seen.value(),
831 "] objective_preference: ", use_obj ?
"true" :
"false",
" ",
845 std::vector<bool>* solution) {
852 if (!solution->empty()) {
861 objective - 1, solver)) {
884 logger.Log(CnfObjectiveLine(problem, objective));
890 std::vector<bool>* solution) {
892 std::deque<EncodingNode> repository;
896 std::vector<EncodingNode*>
nodes =
907 if (!solution->empty()) {
914 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
922 logger.Log(absl::StrFormat(
"c encoding depth:%d", root->
depth()));
928 const int index = offset.value() + objective.value();
952 logger.Log(CnfObjectiveLine(problem, objective));
958 std::vector<bool>* solution) {
964 std::deque<EncodingNode> repository;
965 std::vector<EncodingNode*>
nodes =
972 if (!solution->empty()) {
978 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
988 stratified_lower_bound =
std::max(stratified_lower_bound, n->weight());
994 std::string previous_core_info =
"";
995 for (
int iter = 0;; ++iter) {
1001 const std::string gap_string =
1006 absl::StrFormat(
"c iter:%d [%s] lb:%d%s assumptions:%u depth:%d", iter,
1010 gap_string,
nodes.size(), max_depth));
1017 std::vector<bool> temp_solution;
1022 *solution = temp_solution;
1023 logger.Log(CnfObjectiveLine(problem, obj));
1029 stratified_lower_bound =
1031 if (stratified_lower_bound > 0)
continue;
1043 previous_core_info =
1044 absl::StrFormat(
"core:%u mw:%d", core.size(), min_weight.value());
1047 if (stratified_lower_bound < min_weight &&
1050 stratified_lower_bound = min_weight;
1059 IntegerVariable objective_var,
1060 const std::function<
void()>& feasible_solution_observer,
Model*
model) {
1071 const IntegerValue objective = integer_trail->LowerBound(objective_var);
1074 if (feasible_solution_observer !=
nullptr) {
1075 feasible_solution_observer();
1083 if (!integer_trail->Enqueue(
1092 IntegerVariable objective_var,
1093 const std::function<
void()>& feasible_solution_observer,
Model*
model) {
1110 IntegerValue unknown_min = integer_trail->UpperBound(objective_var);
1111 IntegerValue unknown_max = integer_trail->LowerBound(objective_var);
1113 sat_solver->Backtrack(0);
1114 const IntegerValue lb = integer_trail->LowerBound(objective_var);
1115 const IntegerValue ub = integer_trail->UpperBound(objective_var);
1116 unknown_min =
std::min(unknown_min, ub);
1117 unknown_max =
std::max(unknown_max, lb);
1120 IntegerValue target;
1121 if (lb < unknown_min) {
1122 target = lb + (unknown_min - lb) / 2;
1123 }
else if (unknown_max < ub) {
1124 target = ub - (ub - unknown_max) / 2;
1126 VLOG(1) <<
"Binary-search, done.";
1129 VLOG(1) <<
"Binary-search, objective: [" << lb <<
"," << ub <<
"]" 1130 <<
" tried: [" << unknown_min <<
"," << unknown_max <<
"]" 1131 <<
" target: obj<=" << target;
1134 const Literal assumption = integer_encoder->GetOrCreateAssociatedLiteral(
1148 sat_solver->Backtrack(0);
1149 if (!integer_trail->Enqueue(
1158 const IntegerValue objective = integer_trail->LowerBound(objective_var);
1159 if (feasible_solution_observer !=
nullptr) {
1160 feasible_solution_observer();
1165 sat_solver->Backtrack(0);
1166 if (!integer_trail->Enqueue(
1174 unknown_min =
std::min(target, unknown_min);
1175 unknown_max =
std::max(target, unknown_max);
1181 sat_solver->Backtrack(0);
1209 std::vector<IntegerValue> assumption_weights,
1210 IntegerValue stratified_threshold, Model*
model,
1211 std::vector<std::vector<Literal>>* cores) {
1213 SatSolver* sat_solver =
model->GetOrCreate<SatSolver>();
1221 std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
1222 if (sat_solver->parameters().minimize_core()) {
1225 if (core.empty())
return sat_solver->UnsatStatus();
1226 cores->push_back(core);
1227 if (!sat_solver->parameters().find_multiple_cores())
break;
1231 std::vector<int> indices;
1233 std::set<Literal> temp(core.begin(), core.end());
1234 for (
int i = 0; i < assumptions.size(); ++i) {
1236 indices.push_back(i);
1246 IntegerValue min_weight = assumption_weights[indices.front()];
1247 for (
const int i : indices) {
1248 min_weight =
std::min(min_weight, assumption_weights[i]);
1250 for (
const int i : indices) {
1251 assumption_weights[i] -= min_weight;
1257 for (
int i = 0; i < assumptions.size(); ++i) {
1258 if (assumption_weights[i] < stratified_threshold)
continue;
1259 assumptions[new_size] = assumptions[i];
1260 assumption_weights[new_size] = assumption_weights[i];
1263 assumptions.resize(new_size);
1264 assumption_weights.resize(new_size);
1265 }
while (!assumptions.empty());
1272 std::vector<Literal> assumptions, Model*
model,
1273 std::vector<std::vector<Literal>>* cores) {
1275 SatSolver* sat_solver =
model->GetOrCreate<SatSolver>();
1276 TimeLimit* limit =
model->GetOrCreate<TimeLimit>();
1283 std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
1284 if (sat_solver->parameters().minimize_core()) {
1287 CHECK(!core.empty());
1288 cores->push_back(core);
1289 if (!sat_solver->parameters().find_multiple_cores())
break;
1293 CHECK(!core.empty());
1294 auto* random =
model->GetOrCreate<ModelRandomGenerator>();
1295 const Literal random_literal =
1296 core[absl::Uniform<int>(*random, 0, core.size())];
1297 for (
int i = 0; i < assumptions.size(); ++i) {
1298 if (assumptions[i] == random_literal) {
1299 std::swap(assumptions[i], assumptions.back());
1300 assumptions.pop_back();
1304 }
while (!assumptions.empty());
1311 IntegerVariable objective_var,
1312 const std::vector<IntegerVariable>& variables,
1314 std::function<
void()> feasible_solution_observer,
Model*
model)
1321 objective_var_(objective_var),
1322 feasible_solution_observer_(std::move(feasible_solution_observer)) {
1324 for (
int i = 0; i < variables.size(); ++i) {
1332 terms_.back().depth = 0;
1344 bool CoreBasedOptimizer::ProcessSolution() {
1347 IntegerValue objective(0);
1348 for (ObjectiveTerm& term : terms_) {
1350 objective += term.weight *
value;
1365 if (feasible_solution_observer_ !=
nullptr) {
1366 feasible_solution_observer_();
1376 return integer_trail_->
Enqueue(
1380 bool CoreBasedOptimizer::PropagateObjectiveBounds() {
1382 bool some_bound_were_tightened =
true;
1383 while (some_bound_were_tightened) {
1384 some_bound_were_tightened =
false;
1388 IntegerValue implied_objective_lb(0);
1389 for (ObjectiveTerm& term : terms_) {
1390 const IntegerValue var_lb = integer_trail_->
LowerBound(term.var);
1391 term.old_var_lb = var_lb;
1392 implied_objective_lb += term.weight * var_lb.value();
1396 if (implied_objective_lb > integer_trail_->
LowerBound(objective_var_)) {
1398 objective_var_, implied_objective_lb),
1403 some_bound_were_tightened =
true;
1412 const IntegerValue gap =
1413 integer_trail_->
UpperBound(objective_var_) - implied_objective_lb;
1415 for (
const ObjectiveTerm& term : terms_) {
1416 if (term.weight == 0)
continue;
1417 const IntegerValue var_lb = integer_trail_->
LowerBound(term.var);
1418 const IntegerValue var_ub = integer_trail_->
UpperBound(term.var);
1419 if (var_lb == var_ub)
continue;
1426 if (gap / term.weight < var_ub - var_lb) {
1427 some_bound_were_tightened =
true;
1428 const IntegerValue new_ub = var_lb + gap / term.weight;
1450 void CoreBasedOptimizer::ComputeNextStratificationThreshold() {
1451 std::vector<IntegerValue> weights;
1452 for (ObjectiveTerm& term : terms_) {
1453 if (term.weight >= stratification_threshold_)
continue;
1454 if (term.weight == 0)
continue;
1458 if (var_lb == var_ub)
continue;
1460 weights.push_back(term.weight);
1462 if (weights.empty()) {
1463 stratification_threshold_ = IntegerValue(0);
1468 stratification_threshold_ =
1469 weights[static_cast<int>(std::floor(0.9 * weights.size()))];
1472 bool CoreBasedOptimizer::CoverOptimization() {
1475 constexpr
double max_dtime_per_core = 0.5;
1482 for (
const ObjectiveTerm& term : terms_) {
1486 if (term.depth == 0)
continue;
1492 const IntegerVariable
var = term.var;
1503 const double deterministic_limit =
1515 VLOG(1) <<
"cover_opt var:" <<
var <<
" domain:[" 1517 if (!ProcessSolution())
return false;
1536 if (!PropagateObjectiveBounds())
return false;
1545 std::map<LiteralIndex, int> literal_to_term_index;
1564 std::vector<int> term_indices;
1565 std::vector<IntegerLiteral> integer_assumptions;
1566 std::vector<IntegerValue> assumption_weights;
1567 IntegerValue objective_offset(0);
1568 bool some_assumptions_were_skipped =
false;
1569 for (
int i = 0; i < terms_.size(); ++i) {
1570 const ObjectiveTerm term = terms_[i];
1573 if (term.weight == 0)
continue;
1579 const IntegerValue var_lb = integer_trail_->
LowerBound(term.var);
1580 const IntegerValue var_ub = integer_trail_->
UpperBound(term.var);
1581 if (var_lb == var_ub) {
1582 objective_offset += term.weight * var_lb.value();
1587 if (term.weight >= stratification_threshold_) {
1588 integer_assumptions.push_back(
1590 assumption_weights.push_back(term.weight);
1591 term_indices.push_back(i);
1593 some_assumptions_were_skipped =
true;
1598 if (term_indices.empty() && some_assumptions_were_skipped) {
1599 ComputeNextStratificationThreshold();
1604 if (term_indices.size() <= 2 && !some_assumptions_were_skipped) {
1605 VLOG(1) <<
"Switching to linear scan...";
1606 if (!already_switched_to_linear_scan_) {
1607 already_switched_to_linear_scan_ =
true;
1608 std::vector<IntegerVariable> constraint_vars;
1609 std::vector<int64_t> constraint_coeffs;
1610 for (
const int index : term_indices) {
1611 constraint_vars.push_back(terms_[
index].
var);
1612 constraint_coeffs.push_back(terms_[
index].
weight.value());
1614 constraint_vars.push_back(objective_var_);
1615 constraint_coeffs.push_back(-1);
1617 -objective_offset.value()));
1621 objective_var_, feasible_solution_observer_, model_);
1627 for (
const ObjectiveTerm& term : terms_) {
1628 max_depth =
std::max(max_depth, term.depth);
1630 const int64_t lb = integer_trail_->
LowerBound(objective_var_).value();
1631 const int64_t ub = integer_trail_->
UpperBound(objective_var_).value();
1635 : static_cast<int>(std::ceil(
1636 100.0 * (ub - lb) /
std::max(std::abs(ub), std::abs(lb))));
1637 VLOG(1) << absl::StrCat(
"unscaled_next_obj_range:[", lb,
",", ub,
1640 gap,
"%",
" assumptions:", term_indices.size(),
1641 " strat:", stratification_threshold_.value(),
1642 " depth:", max_depth);
1646 std::vector<Literal> assumptions;
1647 literal_to_term_index.clear();
1648 for (
int i = 0; i < integer_assumptions.size(); ++i) {
1650 integer_assumptions[i]));
1658 literal_to_term_index[assumptions.back().Index()] = term_indices[i];
1666 std::vector<std::vector<Literal>> cores;
1668 FindCores(assumptions, assumption_weights, stratification_threshold_,
1674 if (cores.empty()) {
1675 ComputeNextStratificationThreshold();
1684 for (
const std::vector<Literal>& core : cores) {
1687 if (core.size() == 1)
continue;
1692 bool ignore_this_core =
false;
1694 IntegerValue max_weight(0);
1695 IntegerValue new_var_lb(1);
1696 IntegerValue new_var_ub(0);
1698 for (
const Literal lit : core) {
1703 if (terms_[
index].old_var_lb <
1705 ignore_this_core =
true;
1716 if (ignore_this_core)
continue;
1718 VLOG(1) << absl::StrFormat(
1719 "core:%u weight:[%d,%d] domain:[%d,%d] depth:%d", core.size(),
1720 min_weight.value(), max_weight.value(), new_var_lb.value(),
1721 new_var_ub.value(), new_depth);
1725 const IntegerVariable new_var =
1727 terms_.push_back({new_var, min_weight, new_depth});
1728 terms_.back().cover_ub = new_var_ub;
1732 std::vector<IntegerVariable> constraint_vars;
1733 std::vector<int64_t> constraint_coeffs;
1734 for (
const Literal lit : core) {
1736 terms_[
index].weight -= min_weight;
1737 constraint_vars.push_back(terms_[
index].
var);
1738 constraint_coeffs.push_back(1);
1740 constraint_vars.push_back(new_var);
1741 constraint_coeffs.push_back(-1);
1759 const std::function<
void()>& feasible_solution_observer,
Model*
model) {
1760 #if !defined(__PORTABLE_PLATFORM__) && defined(USE_SCIP) 1762 IntegerVariable objective_var = objective_definition.
objective_var;
1763 std::vector<IntegerVariable> variables = objective_definition.
vars;
1771 const auto process_solution = [&]() {
1774 IntegerValue objective(0);
1775 for (
int i = 0; i < variables.size(); ++i) {
1779 if (objective > integer_trail->UpperBound(objective_var))
return true;
1781 if (feasible_solution_observer !=
nullptr) {
1782 feasible_solution_observer();
1789 if (!integer_trail->Enqueue(
1804 const int num_variables_in_objective = variables.size();
1805 for (
int i = 0; i < num_variables_in_objective; ++i) {
1810 const IntegerVariable
var = variables[i];
1828 std::map<LiteralIndex, std::vector<int>> assumption_to_indices;
1831 std::map<std::pair<int, double>,
int> created_var;
1837 for (
int iter = 0;; ++iter) {
1845 const IntegerValue mip_objective(
1846 static_cast<int64_t>(std::round(
response.objective_value())));
1848 <<
" variables: " << hs_model.
variable_size() <<
" hs_lower_bound: " 1850 <<
" strat: " << stratified_threshold;
1856 if (!integer_trail->Enqueue(
1865 std::vector<Literal> assumptions;
1866 assumption_to_indices.clear();
1867 IntegerValue next_stratified_threshold(0);
1868 for (
int i = 0; i < num_variables_in_objective; ++i) {
1869 const IntegerValue hs_value(
1870 static_cast<int64_t>(
response.variable_value(i)));
1871 if (hs_value == integer_trail->UpperBound(variables[i]))
continue;
1875 next_stratified_threshold =
1880 assumptions.push_back(integer_encoder->GetOrCreateAssociatedLiteral(
1882 assumption_to_indices[assumptions.back().Index()].push_back(i);
1887 if (assumptions.empty() && next_stratified_threshold > 0) {
1888 CHECK_LT(next_stratified_threshold, stratified_threshold);
1889 stratified_threshold = next_stratified_threshold;
1898 std::vector<std::vector<Literal>> cores;
1899 result = FindMultipleCoresForMaxHs(assumptions,
model, &cores);
1905 if (cores.empty()) {
1908 stratified_threshold = next_stratified_threshold;
1909 if (stratified_threshold == 0)
break;
1919 for (
const std::vector<Literal>& core : cores) {
1920 if (core.size() == 1) {
1921 for (
const int index :
1924 integer_trail->LowerBound(variables[
index]).value());
1931 ct->set_lower_bound(1.0);
1932 for (
const Literal lit : core) {
1933 for (
const int index :
1935 const double lb = integer_trail->LowerBound(variables[
index]).value();
1937 if (hs_value == lb) {
1939 ct->add_coefficient(1.0);
1940 ct->set_lower_bound(
ct->lower_bound() + lb);
1942 const std::pair<int, double> key = {
index, hs_value};
1945 created_var[key] = new_var_index;
1962 ct->add_coefficient(1.0);
1970 #else // !__PORTABLE_PLATFORM__ && USE_SCIP 1972 #endif // !__PORTABLE_PLATFORM__ && USE_SCIP SatSolver::Status SolveWithFuMalik(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
bool AddObjectiveConstraint(const LinearBooleanProblem &problem, bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, SatSolver *solver)
void set_lower_bound(double value)
int constraint_size() const
void SetParameters(const SatParameters ¶meters)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
void RandomizeDecisionHeuristic(absl::BitGenRef random, SatParameters *parameters)
void set_max_time_in_seconds(double value)
const Collection::value_type::second_type & FindOrDieNoPrint(const Collection &collection, const typename Collection::value_type::first_type &key)
SatSolver::Status SolveWithCardinalityEncoding(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
IntegerVariable AddIntegerVariable(IntegerValue lower_bound, IntegerValue upper_bound)
const SatParameters & parameters() const
double AddOffsetAndScaleObjectiveValue(const LinearBooleanProblem &problem, Coefficient v)
std::function< void(Model *)> WeightedSumLowerOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
void ResetDecisionHeuristic()
std::vector< IntegerVariable > vars
const Coefficient kCoefficientMax(std::numeric_limits< Coefficient::ValueType >::max())
#define CHECK_GE(val1, val2)
Class that owns everything related to a particular optimization model.
ModelSharedTimeLimit * time_limit
int32_t binary_search_num_conflicts() const
std::vector< Literal > GetLastIncompatibleDecisions()
bool minimize_core() const
void SetAssumptionLevel(int assumption_level)
#define CHECK_GT(val1, val2)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
EncodingNode * MergeAllNodesWithDeque(Coefficient upper_bound, const std::vector< EncodingNode * > &nodes, SatSolver *solver, std::deque< EncodingNode > *repository)
#define VLOG(verboselevel)
SatSolver::Status MinimizeWithHittingSetAndLazyEncoding(const ObjectiveDefinition &objective_definition, const std::function< void()> &feasible_solution_observer, Model *model)
IntegerValue LowerBound(IntegerVariable i) const
bool LiteralIsFalse(Literal literal) const
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
bool LiteralIsTrue(Literal literal) const
double max_time_in_seconds() const
bool cover_optimization() const
void set_max_deterministic_time(double value)
const ::operations_research::sat::LinearObjective & objective() const
int EnqueueDecisionAndBacktrackOnConflict(Literal true_literal)
void MinimizeCore(SatSolver *solver, std::vector< Literal > *core)
static constexpr SolverType SCIP_MIXED_INTEGER_PROGRAMMING
SatSolver::Status SolveWithRandomParameters(LogBehavior log, const LinearBooleanProblem &problem, int num_times, absl::BitGenRef random, SatSolver *solver, std::vector< bool > *solution)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void SetNumVariables(int num_variables)
bool AddUnitClause(Literal true_literal)
void set_solver_specific_parameters(ArgT0 &&arg0, ArgT... args)
int constraints_size() const
CoreBasedOptimizer(IntegerVariable objective_var, const std::vector< IntegerVariable > &variables, const std::vector< IntegerValue > &coefficients, std::function< void()> feasible_solution_observer, Model *model)
bool AddBinaryClause(Literal a, Literal b)
std::vector< IntegerValue > coeffs
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
void ExtractAssignment(const LinearBooleanProblem &problem, const SatSolver &solver, std::vector< bool > *assignment)
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Coefficient ComputeCoreMinWeight(const std::vector< EncodingNode * > &nodes, const std::vector< Literal > &core)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
void set_upper_bound(double value)
#define CHECK_LT(val1, val2)
double ScaleIntegerObjective(IntegerValue value) const
void add_coefficient(double value)
bool AddTernaryClause(Literal a, Literal b, Literal c)
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
::operations_research::MPConstraintProto * add_constraint()
int variable_size() const
SatSolver::Status MinimizeIntegerVariableWithLinearScanAndLazyEncoding(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
void add_var_index(int32_t value)
int EnqueueDecisionAndBackjumpOnConflict(Literal true_literal)
void set_is_integer(bool value)
#define CHECK_LE(val1, val2)
bool LimitReached() const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
SatSolver::Status SolveWithLinearScan(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
int32_t num_variables() const
bool ContainsKey(const Collection &collection, const Key &key)
int literals_size() const
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
bool IsAssignmentValid(const LinearBooleanProblem &problem, const std::vector< bool > &assignment)
IntegerVariable objective_var
void Backtrack(int target_level)
int32_t literals(int index) const
bool AddLinearConstraint(bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, std::vector< LiteralWithCoeff > *cst)
SharedResponseManager * response
Status ResetAndSolveWithGivenAssumptions(const std::vector< Literal > &assumptions)
Coefficient ComputeObjectiveValue(const LinearBooleanProblem &problem, const std::vector< bool > &assignment)
static constexpr MaxSatStratificationAlgorithm STRATIFICATION_NONE
Coefficient MaxNodeWeightSmallerThan(const std::vector< EncodingNode * > &nodes, Coefficient upper_bound)
#define CHECK_EQ(val1, val2)
::operations_research::MPModelProto * mutable_model()
SatSolver::Status SolveWithCardinalityEncodingAndCore(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
std::vector< EncodingNode * > CreateInitialEncodingNodes(const std::vector< Literal > &literals, const std::vector< Coefficient > &coeffs, Coefficient *offset, std::deque< EncodingNode > *repository)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
const VariablesAssignment & Assignment() const
Literal literal(int i) const
#define DCHECK(condition)
void set_lower_bound(double value)
SatSolver::Status SolveIntegerProblem(Model *model)
double GetTimeLeft() const
double GetElapsedDeterministicTime() const
Returns the elapsed deterministic time since the construction of this object.
SatSolver::Status ResetAndSolveIntegerProblem(const std::vector< Literal > &assumptions, Model *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 LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
std::tuple< int64_t, int64_t, const double > Coefficient
bool IsCurrentlyIgnored(IntegerVariable i) const
::operations_research::MPVariableProto * add_variable()
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
void set_objective_coefficient(double value)
std::string ProtobufShortDebugString(const P &message)
::operations_research::MPVariableProto * mutable_variable(int index)
void RestrictObjectiveDomainWithBinarySearch(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
IntegerValue UpperBound(IntegerVariable i) const
SatSolver::Status SolveWithWPM1(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
int CurrentDecisionLevel() const
bool AddProblemClause(absl::Span< const Literal > literals)
bool IsModelUnsat() const
Collection of objects used to extend the Constraint Solver library.
void UseObjectiveForSatAssignmentPreference(const LinearBooleanProblem &problem, SatSolver *solver)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
absl::Span< const double > coefficients
int coefficients_size() const
void ProcessCore(const std::vector< Literal > &core, Coefficient min_weight, std::deque< EncodingNode > *repository, std::vector< EncodingNode * > *nodes, SatSolver *solver)
void set_random_seed(int32_t value)
::operations_research::sat::SatParameters_MaxSatStratificationAlgorithm max_sat_stratification() const
int MoveOneUnprocessedLiteralLast(const std::set< LiteralIndex > &processed, int relevant_prefix_size, std::vector< Literal > *literals)
void set_solver_type(::operations_research::MPModelRequest_SolverType value)
#define VLOG_IS_ON(verboselevel)
SatSolver::Status Optimize()
static constexpr MaxSatStratificationAlgorithm STRATIFICATION_DESCENT
static void SolveWithProto(const MPModelRequest &model_request, MPSolutionResponse *response, std::atomic< bool > *interrupt=nullptr)
Solves the model encoded by a MPModelRequest protocol buffer and fills the solution encoded as a MPSo...
void set_log_search_progress(bool value)
void set_max_number_of_conflicts(int64_t value)
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
void MinimizeCoreWithPropagation(TimeLimit *limit, SatSolver *solver, std::vector< Literal > *core)
#define CHECK_NE(val1, val2)
bool stop_after_first_solution() const
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
int64_t coefficients(int index) const
std::vector< Literal > ReduceNodesAndExtractAssumptions(Coefficient upper_bound, Coefficient stratified_lower_bound, Coefficient *lower_bound, std::vector< EncodingNode * > *nodes, SatSolver *solver)
double max_deterministic_time() const
static constexpr MaxSatStratificationAlgorithm STRATIFICATION_ASCENT