27 #include "absl/random/random.h"
28 #include "absl/strings/str_cat.h"
29 #include "absl/strings/str_format.h"
37 #if !defined(__PORTABLE_PLATFORM__) && defined(USE_SCIP)
40 #endif // __PORTABLE_PLATFORM__
61 void Log(
const std::string&
message) {
75 std::string CnfObjectiveLine(
const LinearBooleanProblem& problem,
76 Coefficient objective) {
77 const double scaled_objective =
79 return absl::StrFormat(
"o %d",
static_cast<int64>(scaled_objective));
82 struct LiteralWithCoreIndex {
91 template <
typename Vector>
92 void DeleteVectorIndices(
const std::vector<int>& indices, Vector* v) {
94 int indices_index = 0;
95 for (
int i = 0; i < v->size(); ++i) {
96 if (indices_index < indices.size() && i == indices[indices_index]) {
99 (*v)[new_size] = (*v)[i];
135 class FuMalikSymmetryBreaker {
137 FuMalikSymmetryBreaker() {}
140 void StartResolvingNewCore(
int new_core_index) {
141 literal_by_core_.resize(new_core_index);
142 for (
int i = 0; i < new_core_index; ++i) {
143 literal_by_core_[i].clear();
157 std::vector<Literal> ProcessLiteral(
int assumption_index, Literal
b) {
158 if (assumption_index >= info_by_assumption_index_.size()) {
159 info_by_assumption_index_.resize(assumption_index + 1);
166 std::vector<Literal> result;
167 for (LiteralWithCoreIndex data :
168 info_by_assumption_index_[assumption_index]) {
175 result.insert(result.end(), literal_by_core_[data.core_index].begin(),
176 literal_by_core_[data.core_index].end());
180 for (LiteralWithCoreIndex data :
181 info_by_assumption_index_[assumption_index]) {
182 literal_by_core_[data.core_index].push_back(data.literal);
184 info_by_assumption_index_[assumption_index].push_back(
185 LiteralWithCoreIndex(
b, literal_by_core_.size()));
190 void DeleteIndices(
const std::vector<int>& indices) {
191 DeleteVectorIndices(indices, &info_by_assumption_index_);
196 void ClearInfo(
int assumption_index) {
197 CHECK_LE(assumption_index, info_by_assumption_index_.size());
198 info_by_assumption_index_[assumption_index].clear();
202 void AddInfo(
int assumption_index, Literal
b) {
203 CHECK_GE(assumption_index, info_by_assumption_index_.size());
204 info_by_assumption_index_.resize(assumption_index + 1);
205 info_by_assumption_index_[assumption_index].push_back(
206 LiteralWithCoreIndex(
b, literal_by_core_.size()));
210 std::vector<std::vector<LiteralWithCoreIndex>> info_by_assumption_index_;
211 std::vector<std::vector<Literal>> literal_by_core_;
219 std::vector<Literal> temp = *core;
220 std::reverse(temp.begin(), temp.end());
236 LOG(WARNING) <<
"This should only happen rarely! otherwise, investigate. "
242 if (temp.size() < core->size()) {
243 VLOG(1) <<
"minimization " << core->size() <<
" -> " << temp.size();
244 std::reverse(temp.begin(), temp.end());
250 std::vector<Literal>* core) {
252 std::set<LiteralIndex> moved_last;
253 std::vector<Literal> candidate(core->begin(), core->end());
264 if (target_level == -1)
break;
282 if (candidate.empty() || solver->
IsModelUnsat())
return;
283 moved_last.insert(candidate.back().Index());
288 if (candidate.size() < core->size()) {
289 VLOG(1) <<
"minimization " << core->size() <<
" -> " << candidate.size();
290 core->assign(candidate.begin(), candidate.end());
300 const LinearBooleanProblem& problem,
302 std::vector<bool>* solution) {
304 FuMalikSymmetryBreaker symmetry;
325 std::vector<std::vector<Literal>> blocking_clauses;
326 std::vector<Literal> assumptions;
329 const LinearObjective& objective = problem.objective();
330 CHECK_GT(objective.coefficients_size(), 0);
331 const Coefficient unique_objective_coeff(std::abs(objective.coefficients(0)));
332 for (
int i = 0; i < objective.literals_size(); ++i) {
333 CHECK_EQ(std::abs(objective.coefficients(i)), unique_objective_coeff)
334 <<
"The basic Fu & Malik algorithm needs constant objective coeffs.";
340 blocking_clauses.push_back(std::vector<Literal>(1, min_literal));
343 assumptions.push_back(min_literal);
347 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
348 assumptions.size(), problem.num_variables(),
349 problem.constraints_size()));
356 for (
int iter = 0;; ++iter) {
362 logger.Log(CnfObjectiveLine(problem, objective));
378 logger.Log(absl::StrFormat(
"c iter:%d core:%u", iter, core.size()));
381 if (core.size() == 1) {
385 std::find(assumptions.begin(), assumptions.end(), core[0]) -
387 CHECK_LT(
index, assumptions.size());
398 std::vector<int> to_delete(1,
index);
399 DeleteVectorIndices(to_delete, &assumptions);
400 DeleteVectorIndices(to_delete, &blocking_clauses);
401 symmetry.DeleteIndices(to_delete);
403 symmetry.StartResolvingNewCore(iter);
407 if (core.size() == 2) {
417 std::vector<LiteralWithCoeff> at_most_one_constraint;
418 std::vector<Literal> at_least_one_constraint;
426 for (
int i = 0; i < core.size(); ++i) {
431 std::find(assumptions.begin() +
index, assumptions.end(), core[i]) -
433 CHECK_LT(
index, assumptions.size());
436 const Literal a(BooleanVariable(old_num_variables + i),
true);
437 Literal b(BooleanVariable(old_num_variables + core.size() + i),
true);
438 if (core.size() == 2) {
439 b =
Literal(BooleanVariable(old_num_variables + 2),
true);
440 if (i == 1)
b =
b.Negated();
455 if (assumptions[
index].Variable() >= problem.num_variables()) {
460 blocking_clauses[
index].push_back(
b);
464 blocking_clauses[
index].push_back(
a);
466 blocking_clauses[
index].pop_back();
470 at_least_one_constraint.push_back(
b);
473 assumptions[
index] =
a.Negated();
479 &at_most_one_constraint);
489 LOG(INFO) <<
"Infeasible while adding a clause.";
497 const LinearBooleanProblem& problem,
499 std::vector<bool>* solution) {
501 FuMalikSymmetryBreaker symmetry;
505 Coefficient lower_bound(
static_cast<int64>(problem.objective().offset()));
509 std::vector<Literal> assumptions;
510 std::vector<Coefficient> costs;
511 std::vector<Literal> reference;
514 const LinearObjective& objective = problem.objective();
515 CHECK_GT(objective.coefficients_size(), 0);
516 for (
int i = 0; i < objective.literals_size(); ++i) {
518 const Coefficient coeff(objective.coefficients(i));
524 costs.push_back(coeff);
526 assumptions.push_back(
literal);
527 costs.push_back(-coeff);
528 lower_bound += coeff;
531 reference = assumptions;
534 Coefficient stratified_lower_bound =
535 *std::max_element(costs.begin(), costs.end());
538 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
539 assumptions.size(), problem.num_variables(),
540 problem.constraints_size()));
542 for (
int iter = 0;; ++iter) {
548 const Coefficient hardening_threshold = upper_bound - lower_bound;
549 CHECK_GE(hardening_threshold, 0);
550 std::vector<int> to_delete;
551 int num_above_threshold = 0;
552 for (
int i = 0; i < assumptions.size(); ++i) {
553 if (costs[i] > hardening_threshold) {
557 to_delete.push_back(i);
558 ++num_above_threshold;
562 to_delete.push_back(i);
566 if (!to_delete.empty()) {
567 logger.Log(absl::StrFormat(
"c fixed %u assumptions, %d with cost > %d",
568 to_delete.size(), num_above_threshold,
569 hardening_threshold.value()));
570 DeleteVectorIndices(to_delete, &assumptions);
571 DeleteVectorIndices(to_delete, &costs);
572 DeleteVectorIndices(to_delete, &reference);
573 symmetry.DeleteIndices(to_delete);
578 std::vector<Literal> assumptions_subset;
579 for (
int i = 0; i < assumptions.size(); ++i) {
580 if (costs[i] >= stratified_lower_bound) {
581 assumptions_subset.push_back(assumptions[i]);
593 const Coefficient old_lower_bound = stratified_lower_bound;
594 for (Coefficient
cost : costs) {
595 if (
cost < old_lower_bound) {
596 if (stratified_lower_bound == old_lower_bound ||
597 cost > stratified_lower_bound) {
598 stratified_lower_bound =
cost;
605 const Coefficient objective_offset(
606 static_cast<int64>(problem.objective().offset()));
608 if (objective + objective_offset < upper_bound) {
609 logger.Log(CnfObjectiveLine(problem, objective));
610 upper_bound = objective + objective_offset;
613 if (stratified_lower_bound < old_lower_bound)
continue;
633 for (
int i = 0; i < core.size(); ++i) {
635 std::find(assumptions.begin() +
index, assumptions.end(), core[i]) -
637 CHECK_LT(
index, assumptions.size());
641 lower_bound += min_cost;
644 logger.Log(absl::StrFormat(
645 "c iter:%d core:%u lb:%d min_cost:%d strat:%d", iter, core.size(),
646 lower_bound.value(), min_cost.value(), stratified_lower_bound.value()));
652 if (min_cost > stratified_lower_bound) {
653 stratified_lower_bound = min_cost;
657 if (core.size() == 1) {
661 std::find(assumptions.begin(), assumptions.end(), core[0]) -
663 CHECK_LT(
index, assumptions.size());
671 std::vector<int> to_delete(1,
index);
672 DeleteVectorIndices(to_delete, &assumptions);
673 DeleteVectorIndices(to_delete, &costs);
674 DeleteVectorIndices(to_delete, &reference);
675 symmetry.DeleteIndices(to_delete);
677 symmetry.StartResolvingNewCore(iter);
681 if (core.size() == 2) {
691 std::vector<LiteralWithCoeff> at_most_one_constraint;
692 std::vector<Literal> at_least_one_constraint;
700 for (
int i = 0; i < core.size(); ++i) {
705 std::find(assumptions.begin() +
index, assumptions.end(), core[i]) -
707 CHECK_LT(
index, assumptions.size());
710 const Literal a(BooleanVariable(old_num_variables + i),
true);
711 Literal b(BooleanVariable(old_num_variables + core.size() + i),
true);
712 if (core.size() == 2) {
713 b =
Literal(BooleanVariable(old_num_variables + 2),
true);
714 if (i == 1)
b =
b.Negated();
735 CHECK_GE(costs[
index], min_cost);
736 if (costs[
index] == min_cost) {
738 assumptions[
index] =
a.Negated();
748 symmetry.AddInfo(assumptions.size(),
b);
749 symmetry.ClearInfo(
index);
752 costs[
index] -= min_cost;
760 assumptions.push_back(
a.Negated());
761 costs.push_back(min_cost);
762 reference.push_back(reference[
index]);
774 at_least_one_constraint.push_back(reference[
index].Negated());
780 &at_most_one_constraint);
786 LOG(INFO) <<
"Unsat while adding a clause.";
794 const LinearBooleanProblem& problem,
796 std::vector<bool>* solution) {
798 const SatParameters initial_parameters = solver->
parameters();
801 SatParameters
parameters = initial_parameters;
806 int max_number_of_conflicts = 5;
811 Coefficient best(min_seen);
812 for (
int i = 0; i < num_times; ++i) {
816 parameters.set_max_number_of_conflicts(max_number_of_conflicts);
822 const bool use_obj = absl::Bernoulli(random, 1.0 / 4);
841 std::vector<bool> candidate;
845 if (objective < best) {
846 *solution = candidate;
848 logger.Log(CnfObjectiveLine(problem, objective));
853 objective - 1, solver)) {
857 min_seen =
std::min(min_seen, objective);
858 max_seen =
std::max(max_seen, objective);
860 logger.Log(absl::StrCat(
861 "c ", objective.value(),
" [", min_seen.value(),
", ", max_seen.value(),
862 "] objective_preference: ", use_obj ?
"true" :
"false",
" ",
874 const LinearBooleanProblem& problem,
876 std::vector<bool>* solution) {
883 if (!solution->empty()) {
892 objective - 1, solver)) {
912 const Coefficient old_objective = objective;
914 CHECK_LT(objective, old_objective);
915 logger.Log(CnfObjectiveLine(problem, objective));
921 std::vector<bool>* solution) {
923 std::deque<EncodingNode> repository;
926 Coefficient offset(0);
927 std::vector<EncodingNode*> nodes =
931 CHECK(!nodes.empty());
932 const Coefficient reference = nodes.front()->weight();
933 for (
const EncodingNode* n : nodes) CHECK_EQ(n->weight(), reference);
938 if (!solution->empty()) {
941 upper_bound = objective + offset;
945 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
946 nodes.size(), problem.num_variables(),
947 problem.constraints_size()));
953 logger.Log(absl::StrFormat(
"c encoding depth:%d", root->
depth()));
959 const int index = offset.value() + objective.value();
980 const Coefficient old_objective = objective;
982 CHECK_LT(objective, old_objective);
983 logger.Log(CnfObjectiveLine(problem, objective));
989 std::vector<bool>* solution) {
994 Coefficient offset(0);
995 std::deque<EncodingNode> repository;
996 std::vector<EncodingNode*> nodes =
1001 Coefficient lower_bound(0);
1003 if (!solution->empty()) {
1009 logger.Log(absl::StrFormat(
"c #weights:%u #vars:%d #constraints:%d",
1010 nodes.size(), problem.num_variables(),
1011 problem.constraints_size()));
1014 Coefficient stratified_lower_bound(0);
1016 SatParameters::STRATIFICATION_DESCENT) {
1019 stratified_lower_bound =
std::max(stratified_lower_bound, n->weight());
1025 std::string previous_core_info =
"";
1026 for (
int iter = 0;; ++iter) {
1028 upper_bound, stratified_lower_bound, &lower_bound, &nodes, solver);
1032 const std::string gap_string =
1035 : absl::StrFormat(
" gap:%d", (upper_bound - lower_bound).value());
1037 absl::StrFormat(
"c iter:%d [%s] lb:%d%s assumptions:%u depth:%d", iter,
1039 lower_bound.value() - offset.value() +
1040 static_cast<int64>(problem.objective().offset()),
1041 gap_string, nodes.size(), max_depth));
1048 std::vector<bool> temp_solution;
1052 if (obj + offset < upper_bound) {
1053 *solution = temp_solution;
1054 logger.Log(CnfObjectiveLine(problem, obj));
1055 upper_bound = obj + offset;
1060 stratified_lower_bound =
1062 if (stratified_lower_bound > 0)
continue;
1074 previous_core_info =
1075 absl::StrFormat(
"core:%u mw:%d", core.size(), min_weight.value());
1078 if (stratified_lower_bound < min_weight &&
1080 SatParameters::STRATIFICATION_ASCENT) {
1081 stratified_lower_bound = min_weight;
1084 ProcessCore(core, min_weight, &repository, &nodes, solver);
1085 max_depth =
std::max(max_depth, nodes.back()->depth());
1090 IntegerVariable objective_var,
1091 const std::function<
void()>& feasible_solution_observer,
Model*
model) {
1094 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1102 const IntegerValue objective = integer_trail->LowerBound(objective_var);
1105 if (feasible_solution_observer !=
nullptr) {
1106 feasible_solution_observer();
1108 if (
parameters.stop_after_first_solution()) {
1114 if (!integer_trail->Enqueue(
1123 IntegerVariable objective_var,
1124 const std::function<
void()>& feasible_solution_observer,
Model*
model) {
1125 const SatParameters old_params = *
model->GetOrCreate<SatParameters>();
1132 SatParameters new_params = old_params;
1133 new_params.set_max_number_of_conflicts(
1134 old_params.binary_search_num_conflicts());
1135 *
model->GetOrCreate<SatParameters>() = new_params;
1141 IntegerValue unknown_min = integer_trail->UpperBound(objective_var);
1142 IntegerValue unknown_max = integer_trail->LowerBound(objective_var);
1144 sat_solver->Backtrack(0);
1145 const IntegerValue lb = integer_trail->LowerBound(objective_var);
1146 const IntegerValue ub = integer_trail->UpperBound(objective_var);
1147 unknown_min =
std::min(unknown_min, ub);
1148 unknown_max =
std::max(unknown_max, lb);
1151 IntegerValue target;
1152 if (lb < unknown_min) {
1153 target = lb + (unknown_min - lb) / 2;
1154 }
else if (unknown_max < ub) {
1155 target = ub - (ub - unknown_max) / 2;
1157 VLOG(1) <<
"Binary-search, done.";
1160 VLOG(1) <<
"Binary-search, objective: [" << lb <<
"," << ub <<
"]"
1161 <<
" tried: [" << unknown_min <<
"," << unknown_max <<
"]"
1162 <<
" target: obj<=" << target;
1165 const Literal assumption = integer_encoder->GetOrCreateAssociatedLiteral(
1179 sat_solver->Backtrack(0);
1180 if (!integer_trail->Enqueue(
1189 const IntegerValue objective = integer_trail->LowerBound(objective_var);
1190 if (feasible_solution_observer !=
nullptr) {
1191 feasible_solution_observer();
1196 sat_solver->Backtrack(0);
1197 if (!integer_trail->Enqueue(
1205 unknown_min =
std::min(target, unknown_min);
1206 unknown_max =
std::max(target, unknown_max);
1212 sat_solver->Backtrack(0);
1213 *
model->GetOrCreate<SatParameters>() = old_params;
1240 std::vector<IntegerValue> assumption_weights,
1241 IntegerValue stratified_threshold, Model*
model,
1242 std::vector<std::vector<Literal>>* cores) {
1244 SatSolver* sat_solver =
model->GetOrCreate<SatSolver>();
1252 std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
1253 if (sat_solver->parameters().minimize_core()) {
1256 CHECK(!core.empty());
1257 cores->push_back(core);
1258 if (!sat_solver->parameters().find_multiple_cores())
break;
1262 std::vector<int> indices;
1264 std::set<Literal> temp(core.begin(), core.end());
1265 for (
int i = 0; i < assumptions.size(); ++i) {
1267 indices.push_back(i);
1277 IntegerValue min_weight = assumption_weights[indices.front()];
1278 for (
const int i : indices) {
1279 min_weight =
std::min(min_weight, assumption_weights[i]);
1281 for (
const int i : indices) {
1282 assumption_weights[i] -= min_weight;
1288 for (
int i = 0; i < assumptions.size(); ++i) {
1289 if (assumption_weights[i] < stratified_threshold)
continue;
1290 assumptions[new_size] = assumptions[i];
1291 assumption_weights[new_size] = assumption_weights[i];
1294 assumptions.resize(new_size);
1295 assumption_weights.resize(new_size);
1296 }
while (!assumptions.empty());
1303 std::vector<Literal> assumptions, Model*
model,
1304 std::vector<std::vector<Literal>>* cores) {
1306 SatSolver* sat_solver =
model->GetOrCreate<SatSolver>();
1307 TimeLimit* limit =
model->GetOrCreate<TimeLimit>();
1314 std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
1315 if (sat_solver->parameters().minimize_core()) {
1318 CHECK(!core.empty());
1319 cores->push_back(core);
1320 if (!sat_solver->parameters().find_multiple_cores())
break;
1324 CHECK(!core.empty());
1325 auto* random =
model->GetOrCreate<ModelRandomGenerator>();
1326 const Literal random_literal =
1327 core[absl::Uniform<int>(*random, 0, core.size())];
1328 for (
int i = 0; i < assumptions.size(); ++i) {
1329 if (assumptions[i] == random_literal) {
1330 std::swap(assumptions[i], assumptions.back());
1331 assumptions.pop_back();
1335 }
while (!assumptions.empty());
1342 IntegerVariable objective_var,
1343 const std::vector<IntegerVariable>& variables,
1345 std::function<
void()> feasible_solution_observer,
Model*
model)
1346 : parameters_(
model->GetOrCreate<SatParameters>()),
1352 objective_var_(objective_var),
1353 feasible_solution_observer_(std::move(feasible_solution_observer)) {
1355 for (
int i = 0; i < variables.size(); ++i) {
1363 terms_.back().depth = 0;
1369 stratification_threshold_ = parameters_->max_sat_stratification() ==
1370 SatParameters::STRATIFICATION_NONE
1375 bool CoreBasedOptimizer::ProcessSolution() {
1378 IntegerValue objective(0);
1379 for (ObjectiveTerm& term : terms_) {
1381 objective += term.weight *
value;
1387 if (objective > integer_trail_->
UpperBound(objective_var_))
return true;
1389 if (feasible_solution_observer_ !=
nullptr) {
1390 feasible_solution_observer_();
1392 if (parameters_->stop_after_first_solution()) {
1400 return integer_trail_->
Enqueue(
1404 bool CoreBasedOptimizer::PropagateObjectiveBounds() {
1406 bool some_bound_were_tightened =
true;
1407 while (some_bound_were_tightened) {
1408 some_bound_were_tightened =
false;
1412 IntegerValue implied_objective_lb(0);
1413 for (ObjectiveTerm& term : terms_) {
1414 const IntegerValue var_lb = integer_trail_->
LowerBound(term.var);
1415 term.old_var_lb = var_lb;
1416 implied_objective_lb += term.weight * var_lb.value();
1420 if (implied_objective_lb > integer_trail_->
LowerBound(objective_var_)) {
1422 objective_var_, implied_objective_lb),
1427 some_bound_were_tightened =
true;
1436 const IntegerValue gap =
1437 integer_trail_->
UpperBound(objective_var_) - implied_objective_lb;
1439 for (
const ObjectiveTerm& term : terms_) {
1440 if (term.weight == 0)
continue;
1441 const IntegerValue var_lb = integer_trail_->
LowerBound(term.var);
1442 const IntegerValue var_ub = integer_trail_->
UpperBound(term.var);
1443 if (var_lb == var_ub)
continue;
1450 if (gap / term.weight < var_ub - var_lb) {
1451 some_bound_were_tightened =
true;
1452 const IntegerValue new_ub = var_lb + gap / term.weight;
1453 CHECK_LT(new_ub, var_ub);
1474 void CoreBasedOptimizer::ComputeNextStratificationThreshold() {
1475 std::vector<IntegerValue> weights;
1476 for (ObjectiveTerm& term : terms_) {
1477 if (term.weight >= stratification_threshold_)
continue;
1478 if (term.weight == 0)
continue;
1482 if (var_lb == var_ub)
continue;
1484 weights.push_back(term.weight);
1486 if (weights.empty()) {
1487 stratification_threshold_ = IntegerValue(0);
1492 stratification_threshold_ =
1493 weights[
static_cast<int>(std::floor(0.9 * weights.size()))];
1496 bool CoreBasedOptimizer::CoverOptimization() {
1499 constexpr
double max_dtime_per_core = 0.5;
1500 const double old_time_limit = parameters_->max_deterministic_time();
1501 parameters_->set_max_deterministic_time(max_dtime_per_core);
1503 parameters_->set_max_deterministic_time(old_time_limit);
1506 for (
const ObjectiveTerm& term : terms_) {
1510 if (term.depth == 0)
continue;
1516 const IntegerVariable
var = term.var;
1527 const double deterministic_limit =
1539 VLOG(1) <<
"cover_opt var:" <<
var <<
" domain:["
1541 if (!ProcessSolution())
return false;
1560 if (!PropagateObjectiveBounds())
return false;
1569 std::map<LiteralIndex, int> literal_to_term_index;
1582 if (parameters_->cover_optimization()) {
1588 std::vector<int> term_indices;
1589 std::vector<IntegerLiteral> integer_assumptions;
1590 std::vector<IntegerValue> assumption_weights;
1591 IntegerValue objective_offset(0);
1592 bool some_assumptions_were_skipped =
false;
1593 for (
int i = 0; i < terms_.size(); ++i) {
1594 const ObjectiveTerm term = terms_[i];
1597 if (term.weight == 0)
continue;
1603 const IntegerValue var_lb = integer_trail_->
LowerBound(term.var);
1604 const IntegerValue var_ub = integer_trail_->
UpperBound(term.var);
1605 if (var_lb == var_ub) {
1606 objective_offset += term.weight * var_lb.value();
1611 if (term.weight >= stratification_threshold_) {
1612 integer_assumptions.push_back(
1614 assumption_weights.push_back(term.weight);
1615 term_indices.push_back(i);
1617 some_assumptions_were_skipped =
true;
1622 if (term_indices.empty() && some_assumptions_were_skipped) {
1623 ComputeNextStratificationThreshold();
1628 if (term_indices.size() <= 2 && !some_assumptions_were_skipped) {
1629 VLOG(1) <<
"Switching to linear scan...";
1630 if (!already_switched_to_linear_scan_) {
1631 already_switched_to_linear_scan_ =
true;
1632 std::vector<IntegerVariable> constraint_vars;
1633 std::vector<int64> constraint_coeffs;
1634 for (
const int index : term_indices) {
1635 constraint_vars.push_back(terms_[
index].
var);
1636 constraint_coeffs.push_back(terms_[
index].
weight.value());
1638 constraint_vars.push_back(objective_var_);
1639 constraint_coeffs.push_back(-1);
1641 -objective_offset.value()));
1645 objective_var_, feasible_solution_observer_, model_);
1649 if (VLOG_IS_ON(1)) {
1651 for (
const ObjectiveTerm& term : terms_) {
1652 max_depth =
std::max(max_depth, term.depth);
1659 :
static_cast<int>(std::ceil(
1660 100.0 * (ub - lb) /
std::max(std::abs(ub), std::abs(lb))));
1661 VLOG(1) << absl::StrCat(
"unscaled_next_obj_range:[", lb,
",", ub,
1664 gap,
"%",
" assumptions:", term_indices.size(),
1665 " strat:", stratification_threshold_.value(),
1666 " depth:", max_depth);
1670 std::vector<Literal> assumptions;
1671 literal_to_term_index.clear();
1672 for (
int i = 0; i < integer_assumptions.size(); ++i) {
1674 integer_assumptions[i]));
1682 literal_to_term_index[assumptions.back().Index()] = term_indices[i];
1690 std::vector<std::vector<Literal>> cores;
1692 FindCores(assumptions, assumption_weights, stratification_threshold_,
1698 if (cores.empty()) {
1699 ComputeNextStratificationThreshold();
1708 for (
const std::vector<Literal>& core : cores) {
1711 if (core.size() == 1)
continue;
1716 bool ignore_this_core =
false;
1718 IntegerValue max_weight(0);
1719 IntegerValue new_var_lb(1);
1720 IntegerValue new_var_ub(0);
1722 for (
const Literal lit : core) {
1727 if (terms_[
index].old_var_lb <
1729 ignore_this_core =
true;
1740 if (ignore_this_core)
continue;
1742 VLOG(1) << absl::StrFormat(
1743 "core:%u weight:[%d,%d] domain:[%d,%d] depth:%d", core.size(),
1744 min_weight.value(), max_weight.value(), new_var_lb.value(),
1745 new_var_ub.value(), new_depth);
1749 const IntegerVariable new_var =
1751 terms_.push_back({new_var, min_weight, new_depth});
1752 terms_.back().cover_ub = new_var_ub;
1756 std::vector<IntegerVariable> constraint_vars;
1757 std::vector<int64> constraint_coeffs;
1758 for (
const Literal lit : core) {
1760 terms_[
index].weight -= min_weight;
1761 constraint_vars.push_back(terms_[
index].
var);
1762 constraint_coeffs.push_back(1);
1764 constraint_vars.push_back(new_var);
1765 constraint_coeffs.push_back(-1);
1783 const std::function<
void()>& feasible_solution_observer,
Model*
model) {
1784 #if !defined(__PORTABLE_PLATFORM__) && defined(USE_SCIP)
1786 IntegerVariable objective_var = objective_definition.
objective_var;
1787 std::vector<IntegerVariable> variables = objective_definition.
vars;
1795 const auto process_solution = [&]() {
1798 IntegerValue objective(0);
1799 for (
int i = 0; i < variables.size(); ++i) {
1803 if (objective > integer_trail->UpperBound(objective_var))
return true;
1805 if (feasible_solution_observer !=
nullptr) {
1806 feasible_solution_observer();
1813 if (!integer_trail->Enqueue(
1823 MPModelRequest request;
1824 request.set_solver_specific_parameters(
"limits/gap = 0");
1825 request.set_solver_type(MPModelRequest::SCIP_MIXED_INTEGER_PROGRAMMING);
1827 MPModelProto& hs_model = *request.mutable_model();
1828 const int num_variables_in_objective = variables.size();
1829 for (
int i = 0; i < num_variables_in_objective; ++i) {
1834 const IntegerVariable
var = variables[i];
1835 MPVariableProto* var_proto = hs_model.add_variable();
1836 var_proto->set_lower_bound(integer_trail->LowerBound(
var).value());
1837 var_proto->set_upper_bound(integer_trail->UpperBound(
var).value());
1839 var_proto->set_is_integer(
true);
1852 std::map<LiteralIndex, std::vector<int>> assumption_to_indices;
1855 std::map<std::pair<int, double>,
int> created_var;
1857 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1861 for (
int iter = 0;; ++iter) {
1869 const IntegerValue mip_objective(
1870 static_cast<int64>(std::round(
response.objective_value())));
1871 VLOG(1) <<
"constraints: " << hs_model.constraint_size()
1872 <<
" variables: " << hs_model.variable_size() <<
" hs_lower_bound: "
1874 <<
" strat: " << stratified_threshold;
1880 if (!integer_trail->Enqueue(
1889 std::vector<Literal> assumptions;
1890 assumption_to_indices.clear();
1891 IntegerValue next_stratified_threshold(0);
1892 for (
int i = 0; i < num_variables_in_objective; ++i) {
1893 const IntegerValue hs_value(
1895 if (hs_value == integer_trail->UpperBound(variables[i]))
continue;
1899 next_stratified_threshold =
1904 assumptions.push_back(integer_encoder->GetOrCreateAssociatedLiteral(
1906 assumption_to_indices[assumptions.back().Index()].push_back(i);
1911 if (assumptions.empty() && next_stratified_threshold > 0) {
1912 CHECK_LT(next_stratified_threshold, stratified_threshold);
1913 stratified_threshold = next_stratified_threshold;
1922 std::vector<std::vector<Literal>> cores;
1923 result = FindMultipleCoresForMaxHs(assumptions,
model, &cores);
1926 if (
parameters.stop_after_first_solution()) {
1929 if (cores.empty()) {
1932 stratified_threshold = next_stratified_threshold;
1933 if (stratified_threshold == 0)
break;
1943 for (
const std::vector<Literal>& core : cores) {
1944 if (core.size() == 1) {
1945 for (
const int index :
1947 hs_model.mutable_variable(
index)->set_lower_bound(
1948 integer_trail->LowerBound(variables[
index]).value());
1954 MPConstraintProto*
ct = hs_model.add_constraint();
1955 ct->set_lower_bound(1.0);
1956 for (
const Literal lit : core) {
1957 for (
const int index :
1959 const double lb = integer_trail->LowerBound(variables[
index]).value();
1961 if (hs_value == lb) {
1963 ct->add_coefficient(1.0);
1964 ct->set_lower_bound(
ct->lower_bound() + lb);
1966 const std::pair<int, double> key = {
index, hs_value};
1968 const int new_var_index = hs_model.variable_size();
1969 created_var[key] = new_var_index;
1971 MPVariableProto* new_var = hs_model.add_variable();
1972 new_var->set_lower_bound(0);
1973 new_var->set_upper_bound(1);
1974 new_var->set_is_integer(
true);
1978 MPConstraintProto* implication = hs_model.add_constraint();
1979 implication->set_lower_bound(lb);
1980 implication->add_var_index(
index);
1981 implication->add_coefficient(1.0);
1982 implication->add_var_index(new_var_index);
1983 implication->add_coefficient(lb - hs_value - 1);
1986 ct->add_coefficient(1.0);
1994 #else // !__PORTABLE_PLATFORM__ && USE_SCIP
1995 LOG(FATAL) <<
"Not supported.";
1996 #endif // !__PORTABLE_PLATFORM__ && USE_SCIP