27#include "absl/container/btree_map.h"
28#include "absl/container/btree_set.h"
29#include "absl/container/flat_hash_set.h"
30#include "absl/random/bit_gen_ref.h"
31#include "absl/random/random.h"
32#include "absl/strings/str_cat.h"
33#include "absl/strings/str_format.h"
41#include "ortools/sat/boolean_problem.pb.h"
49#include "ortools/sat/sat_parameters.pb.h"
66 void Log(
const std::string&
message) {
80std::string CnfObjectiveLine(
const LinearBooleanProblem& problem,
82 const double scaled_objective =
84 return absl::StrFormat(
"o %d",
static_cast<int64_t
>(scaled_objective));
87struct LiteralWithCoreIndex {
96template <
typename Vector>
97void DeleteVectorIndices(
const std::vector<int>& indices, Vector* v) {
99 int indices_index = 0;
100 for (
int i = 0; i < v->size(); ++i) {
101 if (indices_index < indices.size() && i == indices[indices_index]) {
104 (*v)[new_size] = (*v)[i];
140class FuMalikSymmetryBreaker {
142 FuMalikSymmetryBreaker() {}
145 void StartResolvingNewCore(
int new_core_index) {
146 literal_by_core_.resize(new_core_index);
147 for (
int i = 0; i < new_core_index; ++i) {
148 literal_by_core_[i].clear();
162 std::vector<Literal> ProcessLiteral(
int assumption_index, Literal
b) {
163 if (assumption_index >= info_by_assumption_index_.size()) {
164 info_by_assumption_index_.resize(assumption_index + 1);
171 std::vector<Literal> result;
172 for (LiteralWithCoreIndex data :
173 info_by_assumption_index_[assumption_index]) {
180 result.insert(result.end(), literal_by_core_[data.core_index].begin(),
181 literal_by_core_[data.core_index].end());
185 for (LiteralWithCoreIndex data :
186 info_by_assumption_index_[assumption_index]) {
187 literal_by_core_[data.core_index].push_back(data.literal);
189 info_by_assumption_index_[assumption_index].push_back(
190 LiteralWithCoreIndex(
b, literal_by_core_.size()));
195 void DeleteIndices(
const std::vector<int>& indices) {
196 DeleteVectorIndices(indices, &info_by_assumption_index_);
201 void ClearInfo(
int assumption_index) {
202 CHECK_LE(assumption_index, info_by_assumption_index_.size());
203 info_by_assumption_index_[assumption_index].clear();
207 void AddInfo(
int assumption_index, Literal
b) {
208 CHECK_GE(assumption_index, info_by_assumption_index_.size());
209 info_by_assumption_index_.resize(assumption_index + 1);
210 info_by_assumption_index_[assumption_index].push_back(
211 LiteralWithCoreIndex(
b, literal_by_core_.size()));
215 std::vector<std::vector<LiteralWithCoreIndex>> info_by_assumption_index_;
216 std::vector<std::vector<Literal>> literal_by_core_;
224 std::vector<Literal>* core) {
226 absl::btree_set<LiteralIndex> moved_last;
227 std::vector<Literal> candidate(core->begin(), core->end());
239 if (target_level == -1)
break;
257 if (candidate.empty() || solver->
IsModelUnsat())
return;
258 moved_last.insert(candidate.back().Index());
263 if (candidate.size() < core->size()) {
264 VLOG(1) <<
"minimization " << core->size() <<
" -> " << candidate.size();
267 absl::flat_hash_set<LiteralIndex> set;
268 for (
const Literal l : candidate) set.insert(l.Index());
270 for (
const Literal l : *core) {
271 if (set.contains(l.Index())) {
272 (*core)[new_size++] = l;
275 core->resize(new_size);
285 const LinearBooleanProblem& problem,
287 std::vector<bool>* solution) {
289 FuMalikSymmetryBreaker symmetry;
310 std::vector<std::vector<Literal>> blocking_clauses;
311 std::vector<Literal> assumptions;
314 const LinearObjective& objective = problem.objective();
315 CHECK_GT(objective.coefficients_size(), 0);
316 const Coefficient unique_objective_coeff(std::abs(objective.coefficients(0)));
317 for (
int i = 0; i < objective.literals_size(); ++i) {
318 CHECK_EQ(std::abs(objective.coefficients(i)), unique_objective_coeff)
319 <<
"The basic Fu & Malik algorithm needs constant objective coeffs.";
325 blocking_clauses.push_back(std::vector<Literal>(1, min_literal));
328 assumptions.push_back(min_literal);
332 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
333 assumptions.size(), problem.num_variables(),
334 problem.constraints_size()));
341 for (
int iter = 0;; ++iter) {
347 logger.Log(CnfObjectiveLine(problem, objective));
363 logger.Log(absl::StrFormat(
"c iter:%d core:%u", iter, core.size()));
366 if (core.size() == 1) {
370 std::find(assumptions.begin(), assumptions.end(), core[0]) -
383 std::vector<int> to_delete(1,
index);
384 DeleteVectorIndices(to_delete, &assumptions);
385 DeleteVectorIndices(to_delete, &blocking_clauses);
386 symmetry.DeleteIndices(to_delete);
388 symmetry.StartResolvingNewCore(iter);
392 if (core.size() == 2) {
402 std::vector<LiteralWithCoeff> at_most_one_constraint;
403 std::vector<Literal> at_least_one_constraint;
411 for (
int i = 0; i < core.size(); ++i) {
416 std::find(assumptions.begin() +
index, assumptions.end(), core[i]) -
421 const Literal a(BooleanVariable(old_num_variables + i),
true);
422 Literal b(BooleanVariable(old_num_variables + core.size() + i),
true);
423 if (core.size() == 2) {
424 b =
Literal(BooleanVariable(old_num_variables + 2),
true);
425 if (i == 1)
b =
b.Negated();
440 if (assumptions[
index].Variable() >= problem.num_variables()) {
445 blocking_clauses[
index].push_back(
b);
449 blocking_clauses[
index].push_back(
a);
451 blocking_clauses[
index].pop_back();
455 at_least_one_constraint.push_back(
b);
458 assumptions[
index] =
a.Negated();
464 &at_most_one_constraint);
474 LOG(
INFO) <<
"Infeasible while adding a clause.";
482 const LinearBooleanProblem& problem,
484 std::vector<bool>* solution) {
486 FuMalikSymmetryBreaker symmetry;
494 std::vector<Literal> assumptions;
495 std::vector<Coefficient> costs;
496 std::vector<Literal> reference;
499 const LinearObjective& objective = problem.objective();
500 CHECK_GT(objective.coefficients_size(), 0);
501 for (
int i = 0; i < objective.literals_size(); ++i) {
509 costs.push_back(
coeff);
511 assumptions.push_back(
literal);
512 costs.push_back(-
coeff);
516 reference = assumptions;
520 *std::max_element(costs.begin(), costs.end());
523 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
524 assumptions.size(), problem.num_variables(),
525 problem.constraints_size()));
527 for (
int iter = 0;; ++iter) {
535 std::vector<int> to_delete;
536 int num_above_threshold = 0;
537 for (
int i = 0; i < assumptions.size(); ++i) {
538 if (costs[i] > hardening_threshold) {
542 to_delete.push_back(i);
543 ++num_above_threshold;
547 to_delete.push_back(i);
551 if (!to_delete.empty()) {
552 logger.Log(absl::StrFormat(
"c fixed %u assumptions, %d with cost > %d",
553 to_delete.size(), num_above_threshold,
554 hardening_threshold.value()));
555 DeleteVectorIndices(to_delete, &assumptions);
556 DeleteVectorIndices(to_delete, &costs);
557 DeleteVectorIndices(to_delete, &reference);
558 symmetry.DeleteIndices(to_delete);
563 std::vector<Literal> assumptions_subset;
564 for (
int i = 0; i < assumptions.size(); ++i) {
565 if (costs[i] >= stratified_lower_bound) {
566 assumptions_subset.push_back(assumptions[i]);
578 const Coefficient old_lower_bound = stratified_lower_bound;
580 if (
cost < old_lower_bound) {
581 if (stratified_lower_bound == old_lower_bound ||
582 cost > stratified_lower_bound) {
583 stratified_lower_bound =
cost;
591 static_cast<int64_t
>(problem.objective().offset()));
594 logger.Log(CnfObjectiveLine(problem, objective));
598 if (stratified_lower_bound < old_lower_bound)
continue;
618 for (
int i = 0; i < core.size(); ++i) {
620 std::find(assumptions.begin() +
index, assumptions.end(), core[i]) -
629 logger.Log(absl::StrFormat(
630 "c iter:%d core:%u lb:%d min_cost:%d strat:%d", iter, core.size(),
631 lower_bound.value(), min_cost.value(), stratified_lower_bound.value()));
637 if (min_cost > stratified_lower_bound) {
638 stratified_lower_bound = min_cost;
642 if (core.size() == 1) {
646 std::find(assumptions.begin(), assumptions.end(), core[0]) -
656 std::vector<int> to_delete(1,
index);
657 DeleteVectorIndices(to_delete, &assumptions);
658 DeleteVectorIndices(to_delete, &costs);
659 DeleteVectorIndices(to_delete, &reference);
660 symmetry.DeleteIndices(to_delete);
662 symmetry.StartResolvingNewCore(iter);
666 if (core.size() == 2) {
676 std::vector<LiteralWithCoeff> at_most_one_constraint;
677 std::vector<Literal> at_least_one_constraint;
685 for (
int i = 0; i < core.size(); ++i) {
690 std::find(assumptions.begin() +
index, assumptions.end(), core[i]) -
695 const Literal a(BooleanVariable(old_num_variables + i),
true);
696 Literal b(BooleanVariable(old_num_variables + core.size() + i),
true);
697 if (core.size() == 2) {
698 b =
Literal(BooleanVariable(old_num_variables + 2),
true);
699 if (i == 1)
b =
b.Negated();
721 if (costs[
index] == min_cost) {
723 assumptions[
index] =
a.Negated();
733 symmetry.AddInfo(assumptions.size(),
b);
734 symmetry.ClearInfo(
index);
737 costs[
index] -= min_cost;
745 assumptions.push_back(
a.Negated());
746 costs.push_back(min_cost);
747 reference.push_back(reference[
index]);
759 at_least_one_constraint.push_back(reference[
index].Negated());
765 &at_most_one_constraint);
771 LOG(
INFO) <<
"Unsat while adding a clause.";
779 LogBehavior log,
const LinearBooleanProblem& problem,
int num_times,
780 absl::BitGenRef random,
SatSolver* solver, std::vector<bool>* solution) {
782 const SatParameters initial_parameters = solver->
parameters();
784 SatParameters
parameters = initial_parameters;
789 int max_number_of_conflicts = 5;
795 for (
int i = 0; i < num_times; ++i) {
799 parameters.set_max_number_of_conflicts(max_number_of_conflicts);
805 const bool use_obj = absl::Bernoulli(random, 1.0 / 4);
824 std::vector<bool> candidate;
828 if (objective < best) {
829 *solution = candidate;
831 logger.Log(CnfObjectiveLine(problem, objective));
836 objective - 1, solver)) {
840 min_seen =
std::min(min_seen, objective);
841 max_seen =
std::max(max_seen, objective);
843 logger.Log(absl::StrCat(
844 "c ", objective.value(),
" [", min_seen.value(),
", ", max_seen.value(),
845 "] objective_preference: ", use_obj ?
"true" :
"false",
" ",
857 const LinearBooleanProblem& problem,
859 std::vector<bool>* solution) {
866 if (!solution->empty()) {
875 objective - 1, solver)) {
898 logger.Log(CnfObjectiveLine(problem, objective));
904 std::vector<bool>* solution) {
906 std::deque<EncodingNode> repository;
910 std::vector<EncodingNode*>
nodes =
921 if (!solution->empty()) {
928 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
929 nodes.size(), problem.num_variables(),
930 problem.constraints_size()));
936 logger.Log(absl::StrFormat(
"c encoding depth:%d", root->
depth()));
942 const int index = offset.value() + objective.value();
966 logger.Log(CnfObjectiveLine(problem, objective));
972 std::vector<bool>* solution) {
978 std::deque<EncodingNode> repository;
979 std::vector<EncodingNode*>
nodes =
986 if (!solution->empty()) {
992 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
993 nodes.size(), problem.num_variables(),
994 problem.constraints_size()));
999 SatParameters::STRATIFICATION_DESCENT) {
1002 stratified_lower_bound =
std::max(stratified_lower_bound, n->weight());
1008 std::string previous_core_info =
"";
1009 for (
int iter = 0;; ++iter) {
1018 const std::string gap_string =
1023 absl::StrFormat(
"c iter:%d [%s] lb:%d%s assumptions:%u depth:%d", iter,
1026 static_cast<int64_t
>(problem.objective().offset()),
1027 gap_string,
nodes.size(), max_depth));
1034 std::vector<bool> temp_solution;
1039 *solution = temp_solution;
1040 logger.Log(CnfObjectiveLine(problem, obj));
1046 stratified_lower_bound =
1048 if (stratified_lower_bound > 0)
continue;
1060 previous_core_info =
1061 absl::StrFormat(
"core:%u mw:%d", core.size(), min_weight.value());
1064 if (stratified_lower_bound < min_weight &&
1066 SatParameters::STRATIFICATION_ASCENT) {
1067 stratified_lower_bound = min_weight;
1076 IntegerVariable objective_var,
1077 const std::function<
void()>& feasible_solution_observer,
Model*
model) {
1080 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1089 const IntegerValue objective = integer_trail->LowerBound(objective_var);
1092 if (feasible_solution_observer !=
nullptr) {
1093 feasible_solution_observer();
1095 if (
parameters.stop_after_first_solution()) {
1101 if (!integer_trail->Enqueue(
1110 IntegerVariable objective_var,
1111 const std::function<
void()>& feasible_solution_observer,
Model*
model) {
1112 const SatParameters old_params = *
model->GetOrCreate<SatParameters>();
1119 SatParameters new_params = old_params;
1120 new_params.set_max_number_of_conflicts(
1121 old_params.binary_search_num_conflicts());
1122 *
model->GetOrCreate<SatParameters>() = new_params;
1128 IntegerValue unknown_min = integer_trail->UpperBound(objective_var);
1129 IntegerValue unknown_max = integer_trail->LowerBound(objective_var);
1131 sat_solver->Backtrack(0);
1132 const IntegerValue lb = integer_trail->LowerBound(objective_var);
1133 const IntegerValue ub = integer_trail->UpperBound(objective_var);
1134 unknown_min =
std::min(unknown_min, ub);
1135 unknown_max =
std::max(unknown_max, lb);
1138 IntegerValue target;
1139 if (lb < unknown_min) {
1140 target = lb + (unknown_min - lb) / 2;
1141 }
else if (unknown_max < ub) {
1142 target = ub - (ub - unknown_max) / 2;
1144 VLOG(1) <<
"Binary-search, done.";
1147 VLOG(1) <<
"Binary-search, objective: [" << lb <<
"," << ub <<
"]"
1148 <<
" tried: [" << unknown_min <<
"," << unknown_max <<
"]"
1149 <<
" target: obj<=" << target;
1152 const Literal assumption = integer_encoder->GetOrCreateAssociatedLiteral(
1166 sat_solver->Backtrack(0);
1167 if (!integer_trail->Enqueue(
1176 const IntegerValue objective = integer_trail->LowerBound(objective_var);
1177 if (feasible_solution_observer !=
nullptr) {
1178 feasible_solution_observer();
1183 sat_solver->Backtrack(0);
1184 if (!integer_trail->Enqueue(
1192 unknown_min =
std::min(target, unknown_min);
1193 unknown_max =
std::max(target, unknown_max);
1199 sat_solver->Backtrack(0);
1200 *
model->GetOrCreate<SatParameters>() = old_params;
1227 std::vector<IntegerValue> assumption_weights,
1228 IntegerValue stratified_threshold, Model*
model,
1229 std::vector<std::vector<Literal>>* cores) {
1231 SatSolver* sat_solver =
model->GetOrCreate<SatSolver>();
1239 std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
1240 if (sat_solver->parameters().minimize_core()) {
1243 if (core.size() == 1) {
1244 if (!sat_solver->AddUnitClause(core[0].Negated())) {
1248 if (core.empty())
return sat_solver->UnsatStatus();
1249 cores->push_back(core);
1250 if (!sat_solver->parameters().find_multiple_cores())
break;
1254 std::vector<int> indices;
1256 absl::btree_set<Literal> temp(core.begin(), core.end());
1257 for (
int i = 0; i < assumptions.size(); ++i) {
1259 indices.push_back(i);
1269 IntegerValue min_weight = assumption_weights[indices.front()];
1270 for (
const int i : indices) {
1271 min_weight =
std::min(min_weight, assumption_weights[i]);
1273 for (
const int i : indices) {
1274 assumption_weights[i] -= min_weight;
1280 for (
int i = 0; i < assumptions.size(); ++i) {
1281 if (assumption_weights[i] < stratified_threshold)
continue;
1282 assumptions[new_size] = assumptions[i];
1283 assumption_weights[new_size] = assumption_weights[i];
1286 assumptions.resize(new_size);
1287 assumption_weights.resize(new_size);
1288 }
while (!assumptions.empty());
1295 IntegerVariable objective_var,
1296 const std::vector<IntegerVariable>& variables,
1298 std::function<
void()> feasible_solution_observer,
Model*
model)
1299 : parameters_(
model->GetOrCreate<SatParameters>()),
1306 objective_var_(objective_var),
1307 feasible_solution_observer_(
std::move(feasible_solution_observer)) {
1309 for (
int i = 0; i < variables.size(); ++i) {
1317 terms_.back().depth = 0;
1323 stratification_threshold_ = parameters_->max_sat_stratification() ==
1324 SatParameters::STRATIFICATION_NONE
1329bool CoreBasedOptimizer::ProcessSolution() {
1332 IntegerValue objective(0);
1333 for (ObjectiveTerm& term : terms_) {
1335 objective += term.weight *
value;
1350 if (feasible_solution_observer_ !=
nullptr) {
1351 feasible_solution_observer_();
1353 if (parameters_->stop_after_first_solution()) {
1361 return integer_trail_->
Enqueue(
1365bool CoreBasedOptimizer::PropagateObjectiveBounds() {
1367 bool some_bound_were_tightened =
true;
1368 while (some_bound_were_tightened) {
1369 some_bound_were_tightened =
false;
1374 IntegerValue implied_objective_lb(0);
1375 for (ObjectiveTerm& term : terms_) {
1376 const IntegerValue var_lb = integer_trail_->
LowerBound(term.var);
1377 term.old_var_lb = var_lb;
1378 implied_objective_lb += term.weight * var_lb.value();
1382 if (implied_objective_lb > integer_trail_->
LowerBound(objective_var_)) {
1384 objective_var_, implied_objective_lb),
1389 some_bound_were_tightened =
true;
1398 const IntegerValue gap =
1399 integer_trail_->
UpperBound(objective_var_) - implied_objective_lb;
1401 for (
const ObjectiveTerm& term : terms_) {
1402 if (term.weight == 0)
continue;
1403 const IntegerValue var_lb = integer_trail_->
LowerBound(term.var);
1404 const IntegerValue var_ub = integer_trail_->
UpperBound(term.var);
1405 if (var_lb == var_ub)
continue;
1412 if (gap / term.weight < var_ub - var_lb) {
1413 some_bound_were_tightened =
true;
1414 const IntegerValue new_ub = var_lb + gap / term.weight;
1436void CoreBasedOptimizer::ComputeNextStratificationThreshold() {
1437 std::vector<IntegerValue> weights;
1438 for (ObjectiveTerm& term : terms_) {
1439 if (term.weight >= stratification_threshold_)
continue;
1440 if (term.weight == 0)
continue;
1444 if (var_lb == var_ub)
continue;
1446 weights.push_back(term.weight);
1448 if (weights.empty()) {
1449 stratification_threshold_ = IntegerValue(0);
1454 stratification_threshold_ =
1455 weights[
static_cast<int>(std::floor(0.9 * weights.size()))];
1458bool CoreBasedOptimizer::CoverOptimization() {
1463 constexpr double max_dtime_per_core = 0.5;
1464 const double old_time_limit = parameters_->max_deterministic_time();
1465 parameters_->set_max_deterministic_time(max_dtime_per_core);
1467 parameters_->set_max_deterministic_time(old_time_limit);
1470 for (
const ObjectiveTerm& term : terms_) {
1474 if (term.depth == 0)
continue;
1480 const IntegerVariable
var = term.var;
1491 const double deterministic_limit =
1503 VLOG(1) <<
"cover_opt var:" <<
var <<
" domain:["
1505 if (!ProcessSolution())
return false;
1526 return PropagateObjectiveBounds();
1530 const std::vector<Literal>& literals,
1542 std::deque<EncodingNode> repository;
1556 stratified_lower_bound =
std::max(stratified_lower_bound, n->weight());
1561 std::string previous_core_info =
"";
1562 for (
int iter = 0;;) {
1567 integer_trail_->
UpperBound(objective_var_).value() - offset.value());
1570 if (assumptions.empty()) {
1571 stratified_lower_bound =
1573 if (stratified_lower_bound > 0)
continue;
1578 const IntegerValue new_obj_lb(
lower_bound.value() + offset.value());
1579 if (new_obj_lb > integer_trail_->
LowerBound(objective_var_)) {
1592 absl::StrFormat(
"bool_core num_cores:%d [%s] assumptions:%u "
1593 "depth:%d fixed_bools:%d/%d",
1594 iter, previous_core_info,
nodes.size(), max_depth,
1595 num_fixed, num_bools),
1612 stratified_lower_bound =
1614 if (stratified_lower_bound > 0)
continue;
1621 if (parameters_->minimize_core()) {
1628 previous_core_info =
1629 absl::StrFormat(
"core:%u mw:%d d:%d", core.size(), min_weight.value(),
1630 nodes.back()->depth());
1643void CoreBasedOptimizer::PresolveObjectiveWithAtMostOne(
1644 std::vector<Literal>* literals, std::vector<Coefficient>*
coefficients,
1648 const int num_literals = implications_->
literal_size();
1663 std::vector<Literal> candidates;
1664 const int num_terms = literals->
size();
1665 for (
int i = 0; i < num_terms; ++i) {
1666 const Literal lit = (*literals)[i];
1674 candidates.push_back(lit.
Negated());
1678 int num_at_most_ones = 0;
1681 std::vector<Literal> at_most_one;
1682 std::vector<std::pair<Literal, Coefficient>> new_obj_terms;
1684 for (
const Literal root : candidates) {
1685 if (weights[root.NegatedIndex()] == 0)
continue;
1686 if (implications_->
WorkDone() > 1e8)
continue;
1689 CHECK_EQ(weights[root.Index()], 0);
1695 {root}, is_candidate, preferences);
1696 if (at_most_one.size() <= 1)
continue;
1704 for (
const Literal lit : at_most_one) {
1706 lb_increase +=
coeff;
1709 lb_increase -= max_coeff;
1711 *offset += lb_increase;
1712 overall_lb_increase += lb_increase;
1714 for (
const Literal lit : at_most_one) {
1715 is_candidate[lit.
Index()] =
false;
1718 weights[lit.
Index()] = new_weight;
1720 if (new_weight > 0) {
1730 new_obj_terms.push_back({new_lit, max_coeff});
1733 at_most_one.push_back(new_lit);
1735 is_candidate.resize(implications_->
literal_size(),
false);
1739 if (overall_lb_increase > 0) {
1741 model_->
GetOrCreate<SharedResponseManager>()->UpdateInnerObjectiveBounds(
1742 absl::StrFormat(
"am1_presolve num_literals:%d num_am1:%d "
1743 "increase:%lld work_done:%lld",
1744 (
int)candidates.size(), num_at_most_ones,
1745 overall_lb_increase.value(), implications_->
WorkDone()),
1746 IntegerValue(offset->value()),
1753 for (
const Literal root : candidates) {
1754 if (weights[root.Index()] > 0) {
1755 CHECK_EQ(weights[root.NegatedIndex()], 0);
1756 literals->push_back(root);
1759 if (weights[root.NegatedIndex()] > 0) {
1760 CHECK_EQ(weights[root.Index()], 0);
1761 literals->push_back(root.Negated());
1765 for (
const auto& [lit,
coeff] : new_obj_terms) {
1766 literals->push_back(lit);
1776 if (!parameters_->interleave_search()) {
1778 std::vector<Literal> literals;
1780 bool all_booleans =
true;
1781 for (
const ObjectiveTerm& term : terms_) {
1782 const IntegerVariable
var = term.var;
1783 const IntegerValue
coeff = term.weight;
1788 }
else if (ub - lb == 1) {
1794 all_booleans =
false;
1808 PresolveObjectiveWithAtMostOne(&literals, &
coefficients, &offset);
1817 absl::btree_map<LiteralIndex, int> literal_to_term_index;
1831 if (parameters_->cover_optimization()) {
1837 std::vector<int> term_indices;
1838 std::vector<IntegerLiteral> integer_assumptions;
1839 std::vector<IntegerValue> assumption_weights;
1840 IntegerValue objective_offset(0);
1841 bool some_assumptions_were_skipped =
false;
1842 for (
int i = 0; i < terms_.size(); ++i) {
1843 const ObjectiveTerm term = terms_[i];
1846 if (term.weight == 0)
continue;
1852 const IntegerValue var_lb = integer_trail_->
LowerBound(term.var);
1853 const IntegerValue var_ub = integer_trail_->
UpperBound(term.var);
1854 if (var_lb == var_ub) {
1855 objective_offset += term.weight * var_lb.value();
1860 if (term.weight >= stratification_threshold_) {
1861 integer_assumptions.push_back(
1863 assumption_weights.push_back(term.weight);
1864 term_indices.push_back(i);
1866 some_assumptions_were_skipped =
true;
1871 if (term_indices.empty() && some_assumptions_were_skipped) {
1872 ComputeNextStratificationThreshold();
1877 if (term_indices.size() <= 2 && !some_assumptions_were_skipped) {
1878 VLOG(1) <<
"Switching to linear scan...";
1879 if (!already_switched_to_linear_scan_) {
1880 already_switched_to_linear_scan_ =
true;
1881 std::vector<IntegerVariable> constraint_vars;
1882 std::vector<int64_t> constraint_coeffs;
1883 for (
const int index : term_indices) {
1884 constraint_vars.push_back(terms_[
index].
var);
1885 constraint_coeffs.push_back(terms_[
index].
weight.value());
1887 constraint_vars.push_back(objective_var_);
1888 constraint_coeffs.push_back(-1);
1890 -objective_offset.value()));
1894 objective_var_, feasible_solution_observer_, model_);
1900 for (
const ObjectiveTerm& term : terms_) {
1901 max_depth =
std::max(max_depth, term.depth);
1903 const int64_t lb = integer_trail_->
LowerBound(objective_var_).value();
1904 const int64_t ub = integer_trail_->
UpperBound(objective_var_).value();
1908 :
static_cast<int>(std::ceil(
1909 100.0 * (ub - lb) /
std::max(std::abs(ub), std::abs(lb))));
1910 VLOG(1) << absl::StrCat(
"unscaled_next_obj_range:[", lb,
",", ub,
1913 gap,
"%",
" assumptions:", term_indices.size(),
1914 " strat:", stratification_threshold_.value(),
1915 " depth:", max_depth,
1920 std::vector<Literal> assumptions;
1921 literal_to_term_index.clear();
1922 for (
int i = 0; i < integer_assumptions.size(); ++i) {
1924 integer_assumptions[i]));
1932 literal_to_term_index[assumptions.back().Index()] = term_indices[i];
1940 std::vector<std::vector<Literal>> cores;
1942 FindCores(assumptions, assumption_weights, stratification_threshold_,
1948 if (cores.empty()) {
1949 ComputeNextStratificationThreshold();
1958 for (
const std::vector<Literal>& core : cores) {
1961 if (core.size() == 1) {
1971 bool ignore_this_core =
false;
1973 IntegerValue max_weight(0);
1974 IntegerValue new_var_lb(1);
1975 IntegerValue new_var_ub(0);
1977 for (
const Literal lit : core) {
1982 if (terms_[
index].old_var_lb <
1984 ignore_this_core =
true;
1995 if (ignore_this_core)
continue;
1997 VLOG(1) << absl::StrFormat(
1998 "core:%u weight:[%d,%d] domain:[%d,%d] depth:%d", core.size(),
1999 min_weight.value(), max_weight.value(), new_var_lb.value(),
2000 new_var_ub.value(), new_depth);
2004 const IntegerVariable new_var =
2006 terms_.push_back({new_var, min_weight, new_depth});
2007 terms_.back().cover_ub = new_var_ub;
2011 std::vector<IntegerVariable> constraint_vars;
2012 std::vector<int64_t> constraint_coeffs;
2013 for (
const Literal lit : core) {
2015 terms_[
index].weight -= min_weight;
2016 constraint_vars.push_back(terms_[
index].
var);
2017 constraint_coeffs.push_back(1);
2019 constraint_vars.push_back(new_var);
2020 constraint_coeffs.push_back(-1);
#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_LT(val1, val2)
#define DCHECK(condition)
#define CHECK_LE(val1, val2)
#define VLOG(verboselevel)
void resize(size_type new_size)
double GetTimeLeft() const
bool LimitReached() const
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.
std::vector< Literal > ExpandAtMostOneWithWeight(const absl::Span< const Literal > at_most_one, const absl::StrongVector< LiteralIndex, bool > &can_be_included, const absl::StrongVector< LiteralIndex, double > &expanded_lp_values)
int64_t literal_size() const
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()
SatSolver::Status OptimizeWithSatEncoding(const std::vector< Literal > &literals, const std::vector< Coefficient > &coefficients, Coefficient offset)
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
LiteralIndex NegatedIndex() const
LiteralIndex Index() 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:
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
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)
BooleanVariable NewBooleanVariable()
const VariablesAssignment & Assignment() const
int64_t NumFixedVariables() const
void SetAssumptionLevel(int assumption_level)
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
ModelSharedTimeLimit * time_limit
absl::Span< const double > coefficients
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
bool ContainsKey(const Collection &collection, const Key &key)
std::tuple< int64_t, int64_t, const double > Coefficient
void RandomizeDecisionHeuristic(absl::BitGenRef random, SatParameters *parameters)
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)
std::function< void(Model *)> WeightedSumLowerOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
std::function< int64_t(const Model &)> LowerBound(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)
SatSolver::Status SolveWithRandomParameters(LogBehavior log, const LinearBooleanProblem &problem, int num_times, absl::BitGenRef random, SatSolver *solver, std::vector< bool > *solution)
void MinimizeCore(SatSolver *solver, std::vector< Literal > *core)
SatSolver::Status SolveIntegerProblem(Model *model)
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)
Coefficient ComputeObjectiveValue(const LinearBooleanProblem &problem, const std::vector< bool > &assignment)
SatSolver::Status SolveWithFuMalik(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
bool ProcessCore(const std::vector< Literal > &core, Coefficient min_weight, std::deque< EncodingNode > *repository, std::vector< EncodingNode * > *nodes, SatSolver *solver)
Coefficient MaxNodeWeightSmallerThan(const std::vector< EncodingNode * > &nodes, Coefficient upper_bound)
int MoveOneUnprocessedLiteralLast(const absl::btree_set< LiteralIndex > &processed, int relevant_prefix_size, std::vector< Literal > *literals)
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)
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)
#define VLOG_IS_ON(verboselevel)