26#include "absl/cleanup/cleanup.h"
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/log/check.h"
31#include "absl/random/bit_gen_ref.h"
32#include "absl/random/random.h"
33#include "absl/strings/str_cat.h"
34#include "absl/strings/str_format.h"
35#include "absl/types/span.h"
42#include "ortools/sat/boolean_problem.pb.h"
51#include "ortools/sat/sat_parameters.pb.h"
63 std::vector<Literal>* core) {
65 absl::btree_set<LiteralIndex> moved_last;
66 std::vector<Literal> candidate(core->begin(), core->end());
78 if (target_level == -1)
break;
97 moved_last.insert(candidate.back().Index());
102 if (candidate.size() < core->size()) {
103 VLOG(1) <<
"minimization with propag " << core->size() <<
" -> "
107 absl::flat_hash_set<LiteralIndex> set;
108 for (
const Literal l : candidate) set.insert(l.Index());
110 for (
const Literal l : *core) {
111 if (set.contains(l.Index())) {
112 (*core)[new_size++] = l;
115 core->resize(new_size);
120 std::vector<Literal>* core) {
124 if (core->size() > 100 || core->size() == 1)
return;
129 const int old_size = core->size();
130 std::vector<Literal> assumptions;
131 absl::flat_hash_set<LiteralIndex> removed_once;
141 for (
int i = core->size(); --
i >= 0;) {
143 const auto [_, inserted] = removed_once.insert(l.
Index());
149 if (to_remove == -1)
break;
152 for (
int i = 0;
i < core->size(); ++
i) {
153 if (
i == to_remove)
continue;
154 assumptions.push_back((*core)[
i]);
164 if (core->size() < old_size) {
165 VLOG(1) <<
"minimization with search " << old_size <<
" -> "
184 {assumption}, 1'000);
202 std::vector<Literal>* core) {
211 (*core)[new_size++] =
lit;
213 core->resize(new_size);
217 IntegerVariable objective_var,
218 const std::function<
void()>& feasible_solution_observer,
Model*
model) {
222 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
231 const IntegerValue objective = integer_trail->LowerBound(objective_var);
234 if (feasible_solution_observer !=
nullptr) {
235 feasible_solution_observer();
243 if (!integer_trail->Enqueue(
252 IntegerVariable objective_var,
253 const std::function<
void()>& feasible_solution_observer,
Model*
model) {
254 const SatParameters old_params = *
model->GetOrCreate<SatParameters>();
261 SatParameters new_params = old_params;
262 new_params.set_max_number_of_conflicts(
263 old_params.binary_search_num_conflicts());
264 *
model->GetOrCreate<SatParameters>() = new_params;
270 IntegerValue unknown_min = integer_trail->UpperBound(objective_var);
271 IntegerValue unknown_max = integer_trail->LowerBound(objective_var);
273 sat_solver->Backtrack(0);
274 const IntegerValue lb = integer_trail->LowerBound(objective_var);
275 const IntegerValue ub = integer_trail->UpperBound(objective_var);
276 unknown_min = std::min(unknown_min, ub);
277 unknown_max = std::max(unknown_max, lb);
281 if (lb < unknown_min) {
282 target = lb + (unknown_min - lb) / 2;
283 }
else if (unknown_max < ub) {
284 target = ub - (ub - unknown_max) / 2;
286 VLOG(1) <<
"Binary-search, done.";
289 VLOG(1) <<
"Binary-search, objective: [" << lb <<
"," << ub <<
"]"
290 <<
" tried: [" << unknown_min <<
"," << unknown_max <<
"]"
291 <<
" target: obj<=" << target;
294 const Literal assumption = integer_encoder->GetOrCreateAssociatedLiteral(
308 sat_solver->Backtrack(0);
309 if (!integer_trail->Enqueue(
318 const IntegerValue objective = integer_trail->LowerBound(objective_var);
319 if (feasible_solution_observer !=
nullptr) {
320 feasible_solution_observer();
325 sat_solver->Backtrack(0);
326 if (!integer_trail->Enqueue(
334 unknown_min = std::min(target, unknown_min);
335 unknown_max = std::max(target, unknown_max);
341 sat_solver->Backtrack(0);
342 *
model->GetOrCreate<SatParameters>() = old_params;
369 std::vector<IntegerValue> assumption_weights,
370 IntegerValue stratified_threshold, Model*
model,
371 std::vector<std::vector<Literal>>* cores) {
373 SatSolver* sat_solver =
model->GetOrCreate<SatSolver>();
381 std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
382 if (sat_solver->parameters().core_minimization_level() > 0) {
385 if (core.size() == 1) {
386 if (!sat_solver->AddUnitClause(core[0].Negated())) {
390 if (core.empty())
return sat_solver->UnsatStatus();
391 cores->push_back(core);
392 if (!sat_solver->parameters().find_multiple_cores())
break;
396 std::vector<int> indices;
398 absl::btree_set<Literal> temp(core.begin(), core.end());
399 for (
int i = 0;
i < assumptions.size(); ++
i) {
400 if (temp.contains(assumptions[
i])) {
401 indices.push_back(
i);
411 IntegerValue min_weight = assumption_weights[indices.front()];
412 for (
const int i : indices) {
413 min_weight = std::min(min_weight, assumption_weights[
i]);
415 for (
const int i : indices) {
416 assumption_weights[
i] -= min_weight;
422 for (
int i = 0;
i < assumptions.size(); ++
i) {
423 if (assumption_weights[
i] < stratified_threshold)
continue;
424 assumptions[new_size] = assumptions[
i];
425 assumption_weights[new_size] = assumption_weights[
i];
428 assumptions.resize(new_size);
429 assumption_weights.resize(new_size);
430 }
while (!assumptions.empty());
437 IntegerVariable objective_var, absl::Span<const IntegerVariable> variables,
439 std::function<
void()> feasible_solution_observer,
Model*
model)
440 : parameters_(
model->GetOrCreate<SatParameters>()),
448 objective_var_(objective_var),
449 feasible_solution_observer_(
std::move(feasible_solution_observer)) {
451 for (
int i = 0;
i < variables.size(); ++
i) {
459 terms_.back().depth = 0;
465 stratification_threshold_ = parameters_->max_sat_stratification() ==
466 SatParameters::STRATIFICATION_NONE
471bool CoreBasedOptimizer::ProcessSolution() {
474 IntegerValue objective(0);
475 for (ObjectiveTerm& term : terms_) {
477 objective += term.weight *
value;
481 term.cover_ub = std::min(term.cover_ub,
value);
491 if (feasible_solution_observer_ !=
nullptr) {
492 feasible_solution_observer_();
494 if (parameters_->stop_after_first_solution()) {
502 return integer_trail_->
Enqueue(
506bool CoreBasedOptimizer::PropagateObjectiveBounds() {
508 bool some_bound_were_tightened =
true;
509 while (some_bound_were_tightened) {
510 some_bound_were_tightened =
false;
515 IntegerValue implied_objective_lb(0);
516 for (ObjectiveTerm& term : terms_) {
517 const IntegerValue var_lb = integer_trail_->
LowerBound(term.var);
518 term.old_var_lb = var_lb;
519 implied_objective_lb += term.weight * var_lb.value();
523 if (implied_objective_lb > integer_trail_->
LowerBound(objective_var_)) {
525 objective_var_, implied_objective_lb),
530 some_bound_were_tightened =
true;
539 const IntegerValue gap =
540 integer_trail_->
UpperBound(objective_var_) - implied_objective_lb;
542 for (
const ObjectiveTerm& term : terms_) {
543 if (term.weight == 0)
continue;
544 const IntegerValue var_lb = integer_trail_->
LowerBound(term.var);
545 const IntegerValue var_ub = integer_trail_->
UpperBound(term.var);
546 if (var_lb == var_ub)
continue;
553 if (gap / term.weight < var_ub - var_lb) {
554 some_bound_were_tightened =
true;
555 const IntegerValue new_ub = var_lb + gap / term.weight;
556 DCHECK_LT(new_ub, var_ub);
576void CoreBasedOptimizer::ComputeNextStratificationThreshold() {
577 std::vector<IntegerValue> weights;
578 for (ObjectiveTerm& term : terms_) {
579 if (term.weight >= stratification_threshold_)
continue;
580 if (term.weight == 0)
continue;
584 if (var_lb == var_ub)
continue;
586 weights.push_back(term.weight);
588 if (weights.empty()) {
589 stratification_threshold_ = IntegerValue(0);
594 stratification_threshold_ =
595 weights[
static_cast<int>(std::floor(0.9 * weights.size()))];
598bool CoreBasedOptimizer::CoverOptimization() {
603 constexpr double max_dtime_per_core = 0.5;
604 const double old_time_limit = parameters_->max_deterministic_time();
605 parameters_->set_max_deterministic_time(max_dtime_per_core);
606 auto cleanup = ::absl::MakeCleanup([old_time_limit,
this]() {
607 parameters_->set_max_deterministic_time(old_time_limit);
610 for (
const ObjectiveTerm& term : terms_) {
614 if (term.depth == 0)
continue;
620 const IntegerVariable
var = term.var;
631 const double deterministic_limit =
643 VLOG(1) <<
"cover_opt var:" <<
var <<
" domain:["
645 if (!ProcessSolution())
return false;
666 return PropagateObjectiveBounds();
670 absl::Span<const Literal> literals, absl::Span<const IntegerVariable> vars,
671 absl::Span<const Coefficient>
coefficients, Coefficient offset) {
685 for (
int i = 0;
i < literals.size(); ++
i) {
693 for (
int i = 0;
i < vars.size(); ++
i) {
695 const IntegerVariable
var = vars[
i];
698 if (var_ub - var_lb == 1) {
707 const int ub =
static_cast<int>(var_ub.value() - var_lb.value());
710 [
var, var_lb,
this](
int x) {
713 var_lb + IntegerValue(
x + 1)));
725 Coefficient stratified_lower_bound(0);
726 if (parameters_->max_sat_stratification() !=
727 SatParameters::STRATIFICATION_NONE) {
729 stratified_lower_bound = std::max(stratified_lower_bound, n->weight());
735 std::string previous_core_info =
"";
736 for (
int iter = 0;;) {
744 integer_trail_->
UpperBound(objective_var_).value() - offset.value());
747 const IntegerValue new_obj_lb(
lower_bound.value() + offset.value());
748 if (new_obj_lb > integer_trail_->
LowerBound(objective_var_)) {
762 "bool_core (num_cores=%d [%s] a=%u d=%d fixed=%d/%d clauses=%s)",
763 iter, previous_core_info, encoder.
nodes().size(), max_depth,
768 if (parameters_->cover_optimization() && encoder.
nodes().size() > 1) {
772 previous_core_info =
"cover";
781 if (stratified_lower_bound > (gap + 2) / 2) {
782 stratified_lower_bound = (gap + 2) / 2;
784 std::vector<Literal> assumptions;
788 if (!assumptions.empty())
break;
790 stratified_lower_bound =
792 if (stratified_lower_bound > 0)
continue;
798 VLOG(2) <<
"[Core] #nodes " << encoder.
nodes().size()
799 <<
" #assumptions:" << assumptions.size()
800 <<
" stratification:" << stratified_lower_bound <<
" gap:" << gap;
819 stratified_lower_bound =
821 if (stratified_lower_bound > 0)
continue;
828 if (parameters_->core_minimization_level() > 0) {
831 if (parameters_->core_minimization_level() > 1) {
842 absl::StrFormat(
"size:%u mw:%d", core.size(), min_weight.value());
846 if (!encoder.
ProcessCore(core, min_weight, gap, &previous_core_info)) {
849 max_depth = std::max(max_depth, encoder.
nodes().back()->depth());
857 Coefficient* offset) {
859 std::vector<std::pair<LiteralIndex, Coefficient>> pairs;
860 const int size = literals->size();
861 for (
int i = 0;
i <
size; ++
i) {
862 pairs.push_back({(*literals)[
i].Index(), (*coefficients)[
i]});
864 std::sort(pairs.begin(), pairs.end());
868 for (
const auto& [
index, coeff] : pairs) {
870 if (pairs[new_size - 1].first ==
index) {
871 pairs[new_size - 1].second += coeff;
873 }
else if (pairs[new_size - 1].first ==
Literal(
index).NegatedIndex()) {
875 pairs[new_size - 1].second -= coeff;
880 pairs[new_size++] = {
index, coeff};
882 pairs.resize(new_size);
887 for (
const auto& [
index, coeff] : pairs) {
891 }
else if (coeff < 0) {
900void CoreBasedOptimizer::PresolveObjectiveWithAtMostOne(
901 std::vector<Literal>* literals, std::vector<Coefficient>*
coefficients,
902 Coefficient* offset) {
920 std::vector<Literal> candidates;
921 const int num_terms = literals->size();
922 for (
int i = 0;
i < num_terms; ++
i) {
923 const Literal
lit = (*literals)[
i];
924 const Coefficient coeff = (*coefficients)[
i];
929 weights[
lit] = coeff;
931 candidates.push_back(
lit.Negated());
932 is_candidate[
lit.NegatedIndex()] =
true;
935 int num_at_most_ones = 0;
936 Coefficient overall_lb_increase(0);
938 std::vector<Literal> at_most_one;
939 std::vector<std::pair<Literal, Coefficient>> new_obj_terms;
941 for (
const Literal root : candidates) {
942 if (weights[root.NegatedIndex()] == 0)
continue;
943 if (implications_->
WorkDone() > 1e8)
continue;
946 CHECK_EQ(weights[root], 0);
952 {root}, is_candidate, preferences);
953 if (at_most_one.size() <= 1)
continue;
959 Coefficient max_coeff(0);
960 Coefficient lb_increase(0);
961 for (
const Literal
lit : at_most_one) {
962 const Coefficient coeff = weights[
lit.NegatedIndex()];
963 lb_increase += coeff;
964 max_coeff = std::max(max_coeff, coeff);
966 lb_increase -= max_coeff;
968 *offset += lb_increase;
969 overall_lb_increase += lb_increase;
971 for (
const Literal
lit : at_most_one) {
972 is_candidate[
lit] =
false;
973 const Coefficient new_weight = max_coeff - weights[
lit.NegatedIndex()];
974 CHECK_EQ(weights[
lit], 0);
975 weights[
lit] = new_weight;
976 weights[
lit.NegatedIndex()] = 0;
977 if (new_weight > 0) {
981 is_candidate[
lit.NegatedIndex()] =
true;
987 new_obj_terms.push_back({new_lit, max_coeff});
990 at_most_one.push_back(new_lit);
992 is_candidate.resize(implications_->
literal_size(),
false);
996 if (overall_lb_increase > 0) {
998 model_->
GetOrCreate<SharedResponseManager>()->UpdateInnerObjectiveBounds(
999 absl::StrFormat(
"am1_presolve (num_literals=%d num_am1=%d "
1000 "increase=%lld work_done=%lld)",
1001 (
int)candidates.size(), num_at_most_ones,
1002 overall_lb_increase.value(), implications_->
WorkDone()),
1003 IntegerValue(offset->value()),
1010 for (
const Literal root : candidates) {
1011 if (weights[root] > 0) {
1012 CHECK_EQ(weights[root.NegatedIndex()], 0);
1013 literals->push_back(root);
1016 if (weights[root.NegatedIndex()] > 0) {
1017 CHECK_EQ(weights[root], 0);
1018 literals->push_back(root.Negated());
1022 for (
const auto& [
lit, coeff] : new_obj_terms) {
1023 literals->push_back(
lit);
1033 if (!parameters_->interleave_search()) {
1034 Coefficient offset(0);
1035 std::vector<Literal> literals;
1036 std::vector<IntegerVariable> vars;
1038 bool all_booleans =
true;
1039 IntegerValue
range(0);
1040 for (
const ObjectiveTerm& term : terms_) {
1041 const IntegerVariable
var = term.var;
1042 const IntegerValue coeff = term.weight;
1045 offset += Coefficient((lb * coeff).
value());
1046 if (lb == ub)
continue;
1048 vars.push_back(
var);
1054 all_booleans =
false;
1073 PresolveObjectiveWithAtMostOne(&literals, &
coefficients, &offset);
1085 absl::btree_map<LiteralIndex, int> literal_to_term_index;
1099 if (parameters_->cover_optimization()) {
1105 std::vector<int> term_indices;
1106 std::vector<IntegerLiteral> integer_assumptions;
1107 std::vector<IntegerValue> assumption_weights;
1108 IntegerValue objective_offset(0);
1109 bool some_assumptions_were_skipped =
false;
1110 for (
int i = 0;
i < terms_.size(); ++
i) {
1111 const ObjectiveTerm term = terms_[
i];
1114 if (term.weight == 0)
continue;
1120 const IntegerValue var_lb = integer_trail_->
LowerBound(term.var);
1121 const IntegerValue var_ub = integer_trail_->
UpperBound(term.var);
1122 if (var_lb == var_ub) {
1123 objective_offset += term.weight * var_lb.value();
1128 if (term.weight >= stratification_threshold_) {
1129 integer_assumptions.push_back(
1131 assumption_weights.push_back(term.weight);
1132 term_indices.push_back(
i);
1134 some_assumptions_were_skipped =
true;
1139 if (term_indices.empty() && some_assumptions_were_skipped) {
1140 ComputeNextStratificationThreshold();
1145 if (term_indices.size() <= 2 && !some_assumptions_were_skipped) {
1146 VLOG(1) <<
"Switching to linear scan...";
1147 if (!already_switched_to_linear_scan_) {
1148 already_switched_to_linear_scan_ =
true;
1149 std::vector<IntegerVariable> constraint_vars;
1150 std::vector<int64_t> constraint_coeffs;
1151 for (
const int index : term_indices) {
1152 constraint_vars.push_back(terms_[
index].
var);
1153 constraint_coeffs.push_back(terms_[
index].
weight.value());
1155 constraint_vars.push_back(objective_var_);
1156 constraint_coeffs.push_back(-1);
1158 -objective_offset.value()));
1162 objective_var_, feasible_solution_observer_, model_);
1166 if (VLOG_IS_ON(1)) {
1168 for (
const ObjectiveTerm& term : terms_) {
1169 max_depth = std::max(max_depth, term.depth);
1171 const int64_t lb = integer_trail_->
LowerBound(objective_var_).value();
1172 const int64_t ub = integer_trail_->
UpperBound(objective_var_).value();
1176 :
static_cast<int>(std::ceil(
1177 100.0 * (ub - lb) / std::max(std::abs(ub), std::abs(lb))));
1178 VLOG(1) << absl::StrCat(
"unscaled_next_obj_range:[", lb,
",", ub,
1181 gap,
"%",
" assumptions:", term_indices.size(),
1182 " strat:", stratification_threshold_.value(),
1183 " depth:", max_depth,
1188 std::vector<Literal> assumptions;
1189 literal_to_term_index.clear();
1190 for (
int i = 0;
i < integer_assumptions.size(); ++
i) {
1192 integer_assumptions[
i]));
1200 literal_to_term_index[assumptions.back()] = term_indices[
i];
1208 std::vector<std::vector<Literal>> cores;
1210 FindCores(assumptions, assumption_weights, stratification_threshold_,
1216 if (cores.empty()) {
1217 ComputeNextStratificationThreshold();
1226 for (
const std::vector<Literal>& core : cores) {
1229 if (core.size() == 1) {
1239 bool ignore_this_core =
false;
1241 IntegerValue max_weight(0);
1242 IntegerValue new_var_lb(1);
1243 IntegerValue new_var_ub(0);
1246 const int index = literal_to_term_index.at(
lit.Index());
1250 if (terms_[
index].old_var_lb <
1252 ignore_this_core =
true;
1257 min_weight = std::min(min_weight,
weight);
1258 max_weight = std::max(max_weight,
weight);
1259 new_depth = std::max(new_depth, terms_[
index].depth + 1);
1263 if (ignore_this_core)
continue;
1265 VLOG(1) << absl::StrFormat(
1266 "core:%u weight:[%d,%d] domain:[%d,%d] depth:%d", core.size(),
1267 min_weight.value(), max_weight.value(), new_var_lb.value(),
1268 new_var_ub.value(), new_depth);
1272 const IntegerVariable new_var =
1274 terms_.push_back({new_var, min_weight, new_depth});
1275 terms_.back().cover_ub = new_var_ub;
1279 std::vector<IntegerVariable> constraint_vars;
1280 std::vector<int64_t> constraint_coeffs;
1282 const int index = literal_to_term_index.at(
lit.Index());
1283 terms_[
index].weight -= min_weight;
1284 constraint_vars.push_back(terms_[
index].
var);
1285 constraint_coeffs.push_back(1);
1287 constraint_vars.push_back(new_var);
1288 constraint_coeffs.push_back(-1);
bool Contains(int64_t value) const
void EnableLogging(bool enable)
bool LoggingIsEnabled() const
Returns true iff logging is enabled.
double GetElapsedDeterministicTime() const
std::vector< Literal > ExpandAtMostOneWithWeight(absl::Span< const Literal > at_most_one, const util_intops::StrongVector< LiteralIndex, bool > &can_be_included, const util_intops::StrongVector< LiteralIndex, double > &expanded_lp_values)
Same as ExpandAtMostOne() but try to maximize the weight in the clique.
int64_t literal_size() const
int64_t num_clauses() const
SatSolver::Status OptimizeWithSatEncoding(absl::Span< const Literal > literals, absl::Span< const IntegerVariable > vars, absl::Span< const Coefficient > coefficients, Coefficient offset)
SatSolver::Status Optimize()
CoreBasedOptimizer(IntegerVariable objective_var, absl::Span< const IntegerVariable > variables, absl::Span< const IntegerValue > coefficients, std::function< void()> feasible_solution_observer, Model *model)
static EncodingNode GenericNode(int lb, int ub, std::function< Literal(int x)> create_lit, Coefficient weight)
static EncodingNode LiteralNode(Literal l, Coefficient weight)
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
An helper class to share the code used by the different kind of search.
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
const Domain & InitialVariableDomain(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Returns globally valid lower/upper bound on the given integer variable.
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit)
IntegerVariable AddIntegerVariable(IntegerValue lower_bound, IntegerValue upper_bound)
LiteralIndex Index() const
T Add(std::function< T(Model *)> f)
std::vector< EncodingNode * > * mutable_nodes()
void AddBaseNode(EncodingNode node)
const std::vector< EncodingNode * > & nodes() const
bool ProcessCore(absl::Span< const Literal > core, Coefficient min_weight, Coefficient gap, std::string *info)
Status EnqueueDecisionAndBacktrackOnConflict(Literal true_literal, int *first_propagation_index=nullptr)
bool AddProblemClause(absl::Span< const Literal > literals, bool is_safe=true)
SolverLogger * mutable_logger()
Hack to allow to temporarily disable logging if it is enabled.
BooleanVariable NewBooleanVariable()
void NotifyThatModelIsUnsat()
std::vector< Literal > GetLastIncompatibleDecisions()
int EnqueueDecisionAndBackjumpOnConflict(Literal true_literal)
bool ModelIsUnsat() const
void Backtrack(int target_level)
ABSL_MUST_USE_RESULT bool Propagate()
void SetAssumptionLevel(int assumption_level)
ABSL_MUST_USE_RESULT bool AddUnitClause(Literal true_literal)
Status ResetAndSolveWithGivenAssumptions(const std::vector< Literal > &assumptions, int64_t max_number_of_conflicts=-1)
const VariablesAssignment & Assignment() const
int CurrentDecisionLevel() const
ABSL_MUST_USE_RESULT bool ResetToLevelZero()
ABSL_MUST_USE_RESULT bool FinishPropagation()
int64_t NumFixedVariables() const
bool LiteralIsAssigned(Literal literal) const
bool LiteralIsFalse(Literal literal) const
bool LiteralIsTrue(Literal literal) const
void resize(size_type new_size)
absl::Span< const double > coefficients
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::vector< Literal > ExtractAssumptions(Coefficient stratified_lower_bound, const std::vector< EncodingNode * > &nodes, SatSolver *solver)
std::function< void(Model *)> WeightedSumLowerOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
Weighted sum <= constant.
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Returns the vector of the negated variables.
void ReduceNodes(Coefficient upper_bound, Coefficient *lower_bound, std::vector< EncodingNode * > *nodes, SatSolver *solver)
void MinimizeCoreWithPropagation(TimeLimit *limit, SatSolver *solver, std::vector< Literal > *core)
void FilterAssignedLiteral(const VariablesAssignment &assignment, std::vector< Literal > *core)
A core cannot be all true.
void MinimizeCoreWithSearch(TimeLimit *limit, SatSolver *solver, std::vector< Literal > *core)
void PresolveBooleanLinearExpression(std::vector< Literal > *literals, std::vector< Coefficient > *coefficients, Coefficient *offset)
std::string FormatCounter(int64_t num)
Prints a positive number with separators for easier reading (ex: 1'348'065).
bool ProbeLiteral(Literal assumption, SatSolver *solver)
SatSolver::Status MinimizeIntegerVariableWithLinearScanAndLazyEncoding(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
Coefficient ComputeCoreMinWeight(const std::vector< EncodingNode * > &nodes, const std::vector< Literal > &core)
int MoveOneUnprocessedLiteralLast(const absl::btree_set< LiteralIndex > &processed, int relevant_prefix_size, std::vector< Literal > *literals)
SatSolver::Status ResetAndSolveIntegerProblem(const std::vector< Literal > &assumptions, Model *model)
void RestrictObjectiveDomainWithBinarySearch(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
Coefficient MaxNodeWeightSmallerThan(const std::vector< EncodingNode * > &nodes, Coefficient upper_bound)
In SWIG mode, we don't want anything besides these top-level includes.
const std::optional< Range > & range
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)