25#include "absl/base/log_severity.h"
26#include "absl/cleanup/cleanup.h"
27#include "absl/container/flat_hash_map.h"
28#include "absl/container/flat_hash_set.h"
29#include "absl/log/check.h"
30#include "absl/log/log.h"
31#include "absl/log/vlog_is_on.h"
32#include "absl/numeric/int128.h"
33#include "absl/strings/str_cat.h"
34#include "absl/types/span.h"
52 : trail_(model->GetOrCreate<
Trail>()),
57 time_limit_(model->GetOrCreate<
TimeLimit>()),
59 rev_integer_value_repository_(
65 watcher_id_(watcher_->Register(this)),
66 order_(random_, time_limit_,
67 [this](int id) {
return GetVariables(infos_[
id]); }) {
69 integer_trail_->RegisterWatcher(&modified_vars_);
70 integer_trail_->RegisterReversibleClass(
this);
75 watcher_->SetPropagatorPriority(watcher_id_, 0);
79 if (!VLOG_IS_ON(1))
return;
80 if (shared_stats_ ==
nullptr)
return;
81 std::vector<std::pair<std::string, int64_t>> stats;
82 stats.push_back({
"linear_propag/num_pushes", num_pushes_});
84 {
"linear_propag/num_enforcement_pushes", num_enforcement_pushes_});
86 stats.push_back({
"linear_propag/num_cycles", num_cycles_});
87 stats.push_back({
"linear_propag/num_failed_cycles", num_failed_cycles_});
89 stats.push_back({
"linear_propag/num_short_reasons_", num_short_reasons_});
90 stats.push_back({
"linear_propag/num_long_reasons_", num_long_reasons_});
92 stats.push_back({
"linear_propag/num_scanned", num_scanned_});
93 stats.push_back({
"linear_propag/num_explored_in_disassemble",
94 num_explored_in_disassemble_});
95 stats.push_back({
"linear_propag/num_bool_aborts", num_bool_aborts_});
96 stats.push_back({
"linear_propag/num_loop_aborts", num_loop_aborts_});
97 stats.push_back({
"linear_propag/num_ignored", num_ignored_});
98 stats.push_back({
"linear_propag/num_delayed", num_delayed_});
99 shared_stats_->AddStats(stats);
103 if (level < previous_level_) {
105 modified_vars_.ResetAllToFalse();
110 for (
const int id : propagation_queue_) in_queue_.Clear(
id);
111 propagation_queue_.clear();
112 for (
int i = rev_unenforced_size_;
i < unenforced_constraints_.size();
114 in_queue_.Clear(unenforced_constraints_[
i]);
116 unenforced_constraints_.resize(rev_unenforced_size_);
117 }
else if (level > previous_level_) {
118 rev_unenforced_size_ = unenforced_constraints_.size();
119 rev_int_repository_->SaveState(&rev_unenforced_size_);
126 if (!propagation_queue_.empty() ||
127 !modified_vars_.PositionsSetAtLeastOnce().empty() || !order_.IsEmpty()) {
128 watcher_->CallOnNextPropagate(watcher_id_);
131 previous_level_ = level;
134void LinearPropagator::SetPropagatedBy(IntegerVariable var,
int id) {
135 int& ref_id = propagated_by_[var];
136 if (ref_id ==
id)
return;
138 propagated_by_was_set_.
Set(var);
141 DCHECK_LT(var, propagated_by_.size());
143 DCHECK_GE(ref_id, 0);
144 DCHECK_LT(ref_id, id_to_propagation_count_.size());
145 id_to_propagation_count_[ref_id]--;
148 if (
id != -1) id_to_propagation_count_[id]++;
151void LinearPropagator::OnVariableChange(IntegerVariable var, IntegerValue lb,
153 DCHECK_EQ(lb, integer_trail_->
LowerBound(var));
156 const int size = var_to_constraint_ids_[var].size();
157 if (size == 0)
return;
159 SetPropagatedBy(var,
id);
161 Bitset64<int>::View in_queue = in_queue_.
view();
163 for (
const int id : var_to_constraint_ids_[var]) {
164 if (in_queue[
id])
continue;
166 propagation_queue_.push_back(
id);
175 for (
const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
176 if (var >= var_to_constraint_ids_.size())
continue;
177 OnVariableChange(var, integer_trail_->LowerBound(var), -1);
181 num_terms_for_dtime_update_ = 0;
182 const auto cleanup = ::absl::MakeCleanup([
this]() {
183 time_limit_->AdvanceDeterministicTime(
184 static_cast<double>(num_terms_for_dtime_update_) * 1e-9);
185 modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
215 const bool push_affine_ub = push_affine_ub_for_binary_relations_ ||
216 trail_->CurrentDecisionLevel() == 0;
221 const int saved_index = trail_->Index();
222 while (!propagation_queue_.empty()) {
223 const int id = propagation_queue_.front();
224 propagation_queue_.pop_front();
226 const auto [slack, num_to_push] = AnalyzeConstraint(
id);
230 if (!PropagateInfeasibleConstraint(
id, slack))
return false;
237 if (trail_->Index() > saved_index) {
241 }
else if (num_to_push > 0) {
244 const auto vars = GetVariables(infos_[
id]);
245 const auto coeffs = GetCoeffs(infos_[
id]);
246 for (
int i = 0;
i < num_to_push; ++
i) {
247 const IntegerVariable var = vars[
i];
248 const IntegerValue coeff = coeffs[
i];
249 const IntegerValue div = slack / coeff;
250 const IntegerValue new_ub = integer_trail_->LowerBound(var) + div;
251 order_.Register(
id,
NegationOf(var), -new_ub);
270 const ConstraintInfo& info = infos_[id];
271 if (push_affine_ub && info.initial_size == 3 && info.enf_id == -1) {
273 const auto vars = GetVariables(info);
274 const auto coeffs = GetCoeffs(info);
279 if (info.rev_size == 2) {
281 expr.
vars[0] = vars[0];
282 expr.
vars[1] = vars[1];
283 expr.
coeffs[0] = coeffs[0];
284 expr.
coeffs[1] = coeffs[1];
288 const IntegerValue initial_rhs =
289 info.rev_rhs + coeffs[2] * integer_trail_->LowerBound(vars[2]);
290 linear3_bounds_->AddAffineUpperBound(
292 }
else if (info.rev_size == 3) {
293 for (
int i = 0;
i < 3; ++
i) {
295 const int a = (
i + 1) % 3;
296 const int b = (
i + 2) % 3;
297 expr.
vars[0] = vars[a];
298 expr.
vars[1] = vars[
b];
299 expr.
coeffs[0] = coeffs[a];
301 linear3_bounds_->AddAffineUpperBound(
308 const int next_id = order_.NextId();
309 if (next_id == -1)
break;
314 if (!PropagateOneConstraint(next_id))
return false;
319 if (trail_->Index() > saved_index) {
329 absl::Span<const Literal> enforcement_literals,
330 absl::Span<const IntegerVariable> vars,
331 absl::Span<const IntegerValue> coeffs, IntegerValue upper_bound) {
332 if (vars.empty())
return true;
333 if (trail_->CurrentDecisionLevel() == 0) {
334 for (
const Literal l : enforcement_literals) {
335 if (trail_->Assignment().LiteralIsFalse(l))
return true;
341 CHECK_LT(vars.size(), 1 << 29);
342 if (vars.size() > max_variations_.size()) {
343 max_variations_.resize(vars.size(), 0);
344 buffer_of_ones_.resize(vars.size(), IntegerValue(1));
348 CHECK_EQ(vars.size(), coeffs.size());
349 const int id = infos_.size();
352 info.all_coeffs_are_one =
false;
353 info.start = variables_buffer_.size();
354 info.initial_size = vars.size();
355 info.rev_rhs = upper_bound;
356 info.rev_size = vars.size();
357 infos_.push_back(std::move(info));
358 initial_rhs_.push_back(upper_bound);
361 id_to_propagation_count_.push_back(0);
362 variables_buffer_.insert(variables_buffer_.end(), vars.begin(), vars.end());
363 coeffs_buffer_.insert(coeffs_buffer_.end(), coeffs.begin(), coeffs.end());
364 CanonicalizeConstraint(
id);
366 bool all_at_one =
true;
367 for (
const IntegerValue coeff : GetCoeffs(infos_.back())) {
376 infos_.back().all_coeffs_are_one =
true;
381 in_queue_.resize(in_queue_.size() + 1);
383 if (!enforcement_literals.empty()) {
384 infos_.back().enf_status =
386 infos_.back().enf_id = enforcement_propagator_->Register(
387 enforcement_literals,
389 infos_[id].enf_status =
static_cast<int>(status);
395 AddToQueueIfNeeded(
id);
396 watcher_->CallOnNextPropagate(watcher_id_);
404 const auto info = infos_[id];
405 if (info.initial_size == 2) {
406 const auto vars = GetVariables(info);
407 const auto coeffs = GetCoeffs(info);
415 precedences_->SetLevelToTrail();
417 precedences_->PushConditionalRelation(
418 enforcement_propagator_->GetEnforcementLiterals(enf_id),
427 AddToQueueIfNeeded(
id);
428 infos_.back().enf_id = -1;
432 order_.Resize(var_to_constraint_ids_.size(), in_queue_.size());
433 for (
const IntegerVariable var : GetVariables(infos_[
id])) {
435 if (var >= var_to_constraint_ids_.size()) {
437 const int size = std::max(var,
NegationOf(var)).value() + 1;
438 var_to_constraint_ids_.resize(size);
439 propagated_by_.resize(size, -1);
440 propagated_by_was_set_.Resize(IntegerVariable(size));
441 is_watched_.resize(size,
false);
443 order_.Resize(size, in_queue_.size());
449 var_to_constraint_ids_[var].push_back(
id);
453 if (!is_watched_[var]) {
454 is_watched_[var] =
true;
455 watcher_->WatchLowerBound(var, watcher_id_);
468absl::Span<IntegerValue> LinearPropagator::GetCoeffs(
469 const ConstraintInfo& info) {
470 if (info.all_coeffs_are_one) {
471 return absl::MakeSpan(&buffer_of_ones_[0], info.initial_size);
473 return absl::MakeSpan(&coeffs_buffer_[info.start], info.initial_size);
476absl::Span<IntegerVariable> LinearPropagator::GetVariables(
477 const ConstraintInfo& info) {
478 return absl::MakeSpan(&variables_buffer_[info.start], info.initial_size);
481void LinearPropagator::CanonicalizeConstraint(
int id) {
482 const ConstraintInfo& info = infos_[id];
483 const auto coeffs = GetCoeffs(info);
484 const auto vars = GetVariables(info);
485 for (
int i = 0;
i < vars.size(); ++
i) {
487 coeffs[
i] = -coeffs[
i];
495 absl::flat_hash_set<IntegerVariable> no_dup;
496 for (
const IntegerVariable var : GetVariables(info)) {
504std::pair<IntegerValue, int> LinearPropagator::AnalyzeConstraint(
int id) {
508 ConstraintInfo& info = infos_[id];
510 if (
DEBUG_MODE && enforcement_propagator_->PropagationIsDone(*trail_)) {
512 enforcement_propagator_->DebugStatus(info.enf_id);
513 if (enf_status != debug_status) {
520 LOG(FATAL) <<
"Enforcement status not up to date: " << enf_status
521 <<
" vs debug: " << debug_status;
528 DCHECK(!in_queue_[
id]);
533 unenforced_constraints_.push_back(
id);
541 IntegerValue implied_lb(0);
542 const auto vars = GetVariables(info);
543 IntegerValue max_variation(0);
544 bool first_change =
true;
545 num_terms_for_dtime_update_ += info.rev_size;
546 IntegerValue* max_variations = max_variations_.data();
547 const IntegerValue* lower_bounds = integer_trail_->LowerBoundsData();
548 if (info.all_coeffs_are_one) {
550 for (
int i = 0;
i < info.rev_size;) {
551 const IntegerVariable var = vars[
i];
552 const IntegerValue lb = lower_bounds[var.value()];
553 const IntegerValue diff = -lower_bounds[
NegationOf(var).value()] - lb;
558 rev_int_repository_->SaveState(&info.rev_size);
559 rev_integer_value_repository_->SaveState(&info.rev_rhs);
560 first_change =
false;
563 std::swap(vars[
i], vars[info.rev_size]);
567 max_variations[
i] = diff;
568 max_variation = std::max(max_variation, diff);
573 const auto coeffs = GetCoeffs(info);
574 for (
int i = 0;
i < info.rev_size;) {
575 const IntegerVariable var = vars[
i];
576 const IntegerValue coeff = coeffs[
i];
577 const IntegerValue lb = lower_bounds[var.value()];
578 const IntegerValue diff = -lower_bounds[
NegationOf(var).value()] - lb;
583 rev_int_repository_->SaveState(&info.rev_size);
584 rev_integer_value_repository_->SaveState(&info.rev_rhs);
585 first_change =
false;
588 std::swap(vars[
i], vars[info.rev_size]);
589 std::swap(coeffs[
i], coeffs[info.rev_size]);
590 info.rev_rhs -= coeff * lb;
592 implied_lb += coeff * lb;
593 max_variations[
i] = diff * coeff;
594 max_variation = std::max(max_variation, max_variations[
i]);
602 const IntegerValue slack = info.rev_rhs - implied_lb;
607 if (slack < 0 || max_variation <= slack)
return {slack, 0};
611 const auto coeffs = GetCoeffs(info);
612 for (
int i = 0;
i < info.rev_size; ++
i) {
613 if (max_variations[
i] <= slack)
continue;
614 std::swap(vars[
i], vars[num_to_push]);
615 std::swap(coeffs[
i], coeffs[num_to_push]);
618 return {slack, num_to_push};
623bool LinearPropagator::PropagateInfeasibleConstraint(
int id,
624 IntegerValue slack) {
626 const ConstraintInfo& info = infos_[id];
627 const auto vars = GetVariables(info);
628 const auto coeffs = GetCoeffs(info);
631 integer_reason_.clear();
632 reason_coeffs_.clear();
633 for (
int i = 0;
i < info.initial_size; ++
i) {
634 const IntegerVariable var = vars[
i];
635 if (!integer_trail_->VariableLowerBoundIsFromLevelZero(var)) {
636 integer_reason_.push_back(integer_trail_->LowerBoundAsLiteral(var));
637 reason_coeffs_.push_back(coeffs[
i]);
642 integer_trail_->RelaxLinearReason(-slack - 1, reason_coeffs_,
644 ++num_enforcement_pushes_;
645 return enforcement_helper_->PropagateWhenFalse(info.enf_id, {},
651 const ConstraintInfo& info = infos_[id];
653 enforcement_propagator_->GetEnforcementLiterals(info.enf_id);
654 reason->
vars = GetVariables(info);
655 reason->
coeffs = GetCoeffs(info);
658bool LinearPropagator::PropagateOneConstraint(
int id) {
659 const auto [slack, num_to_push] = AnalyzeConstraint(
id);
660 if (slack < 0)
return PropagateInfeasibleConstraint(
id, slack);
661 if (num_to_push == 0)
return true;
663 DCHECK_GT(num_to_push, 0);
665 const ConstraintInfo& info = infos_[id];
666 const auto vars = GetVariables(info);
667 const auto coeffs = GetCoeffs(info);
677 if (!DisassembleSubtree(
id, num_to_push)) {
684 for (
int i = 0;
i < num_to_push; ++
i) {
693 const IntegerVariable var = vars[
i];
694 const IntegerValue coeff = coeffs[
i];
695 const IntegerValue div = slack / coeff;
696 const IntegerValue new_ub = integer_trail_->
LowerBound(var) + div;
697 const IntegerValue propagation_slack = (div + 1) * coeff - slack - 1;
705 const IntegerValue actual_ub = integer_trail_->UpperBound(var);
706 const IntegerVariable next_var =
NegationOf(var);
707 if (actual_ub < new_ub) {
709 OnVariableChange(next_var, -actual_ub, -1);
710 }
else if (actual_ub == new_ub) {
711 OnVariableChange(next_var, -actual_ub,
id);
714 std::swap(vars[
i], vars[num_pushed]);
715 std::swap(coeffs[
i], coeffs[num_pushed]);
726std::string LinearPropagator::ConstraintDebugString(
int id) {
728 const ConstraintInfo& info = infos_[id];
729 const auto coeffs = GetCoeffs(info);
730 const auto vars = GetVariables(info);
731 IntegerValue implied_lb(0);
732 IntegerValue rhs_correction(0);
733 for (
int i = 0;
i < info.initial_size; ++
i) {
734 const IntegerValue term = coeffs[
i] * integer_trail_->LowerBound(vars[
i]);
735 if (
i >= info.rev_size) {
736 rhs_correction += term;
739 absl::StrAppend(&result,
" +", coeffs[
i].value(),
"*X", vars[
i].value());
741 const IntegerValue original_rhs = info.rev_rhs + rhs_correction;
742 absl::StrAppend(&result,
" <= ", original_rhs.value(),
743 " slack=", original_rhs.value() - implied_lb.value());
744 absl::StrAppend(&result,
" enf=", info.enf_status);
748bool LinearPropagator::ReportConflictingCycle() {
756 literal_reason_.clear();
757 integer_reason_.clear();
758 absl::int128 rhs_sum = 0;
759 absl::flat_hash_map<IntegerVariable, absl::int128> map_sum;
760 for (
const auto [
id, next_var, increase] : disassemble_branch_) {
761 const ConstraintInfo& info = infos_[id];
762 enforcement_propagator_->AddEnforcementReason(info.enf_id,
764 const auto coeffs = GetCoeffs(info);
765 const auto vars = GetVariables(info);
766 IntegerValue rhs_correction(0);
767 for (
int i = 0;
i < info.initial_size; ++
i) {
768 if (
i >= info.rev_size) {
769 rhs_correction += coeffs[
i] * integer_trail_->LowerBound(vars[
i]);
772 map_sum[vars[
i]] += coeffs[
i].value();
777 rhs_sum += (info.rev_rhs + rhs_correction).value();
782 absl::int128 implied_lb = 0;
783 for (
const auto [var, coeff] : map_sum) {
785 if (!integer_trail_->VariableLowerBoundIsFromLevelZero(var)) {
786 integer_reason_.push_back(integer_trail_->LowerBoundAsLiteral(var));
789 coeff * absl::int128{integer_trail_->LowerBound(var).value()};
790 }
else if (coeff < 0) {
791 if (!integer_trail_->VariableLowerBoundIsFromLevelZero(
793 integer_reason_.push_back(integer_trail_->UpperBoundAsLiteral(var));
796 coeff * absl::int128{integer_trail_->UpperBound(var).value()};
799 if (implied_lb > rhs_sum) {
801 std::sort(integer_reason_.begin(), integer_reason_.end(),
802 [](
const IntegerLiteral& a,
const IntegerLiteral&
b) {
803 return a.var < b.var;
807 const absl::int128 limit{std::numeric_limits<int64_t>::max()};
808 const absl::int128 slack = implied_lb - rhs_sum;
810 reason_coeffs_.clear();
812 for (
const IntegerLiteral i_lit : integer_reason_) {
819 reason_coeffs_.push_back(
static_cast<int64_t
>(c));
822 const IntegerValue slack64(
823 static_cast<int64_t
>(std::min(limit, slack)));
824 integer_trail_->RelaxLinearReason(slack64 - 1, reason_coeffs_,
829 ++num_short_reasons_;
830 VLOG(2) <<
"Simplified " << integer_reason_.size() <<
" slack "
831 << implied_lb - rhs_sum;
832 return integer_trail_->ReportConflict(literal_reason_, integer_reason_);
841 literal_reason_.clear();
842 integer_reason_.clear();
844 for (
const auto [
id, next_var, increase] : disassemble_branch_) {
845 const ConstraintInfo& info = infos_[id];
846 enforcement_propagator_->AddEnforcementReason(info.enf_id,
848 for (
const IntegerVariable var : GetVariables(infos_[
id])) {
851 if (var == previous_var)
continue;
856 if (!integer_trail_->VariableLowerBoundIsFromLevelZero(var)) {
857 integer_reason_.push_back(integer_trail_->LowerBoundAsLiteral(var));
860 previous_var = next_var;
862 VLOG(2) << next_var <<
" [" << integer_trail_->LowerBound(next_var) <<
","
863 << integer_trail_->UpperBound(next_var)
864 <<
"] : " << ConstraintDebugString(
id);
867 return integer_trail_->ReportConflict(literal_reason_, integer_reason_);
870std::pair<IntegerValue, IntegerValue> LinearPropagator::GetCycleCoefficients(
871 int id, IntegerVariable var, IntegerVariable next_var) {
872 const ConstraintInfo& info = infos_[id];
873 const auto coeffs = GetCoeffs(info);
874 const auto vars = GetVariables(info);
875 IntegerValue var_coeff(0);
876 IntegerValue next_coeff(0);
877 for (
int i = 0;
i < info.initial_size; ++
i) {
878 if (vars[
i] == var) var_coeff = coeffs[
i];
879 if (vars[
i] ==
NegationOf(next_var)) next_coeff = coeffs[
i];
881 DCHECK_NE(var_coeff, 0);
882 DCHECK_NE(next_coeff, 0);
883 return {var_coeff, next_coeff};
895bool LinearPropagator::DisassembleSubtree(
int root_id,
int num_tight) {
901 disassemble_queue_.clear();
902 disassemble_branch_.clear();
904 const ConstraintInfo& info = infos_[root_id];
905 const auto vars = GetVariables(info);
906 for (
int i = 0;
i < num_tight; ++
i) {
907 disassemble_queue_.push_back({root_id,
NegationOf(vars[
i]), 1});
913 absl::Span<int> id_to_count = absl::MakeSpan(id_to_propagation_count_);
914 while (!disassemble_queue_.empty()) {
915 const auto [prev_id, var, increase] = disassemble_queue_.back();
916 if (!disassemble_branch_.empty() &&
917 disassemble_branch_.back().id == prev_id &&
918 disassemble_branch_.back().var == var) {
919 disassemble_branch_.pop_back();
920 disassemble_queue_.pop_back();
924 disassemble_branch_.push_back({prev_id, var, increase});
926 time_limit_->AdvanceDeterministicTime(
927 static_cast<double>(var_to_constraint_ids_[var].size()) * 1e-9);
928 for (
const int id : var_to_constraint_ids_[var]) {
933 CHECK(!disassemble_branch_.empty());
936 const auto [test_id, root_var, var_increase] = disassemble_branch_[0];
937 CHECK_EQ(test_id, root_id);
938 CHECK_NE(var, root_var);
943 const auto [var_coeff, root_coeff] =
944 GetCycleCoefficients(
id, var, root_var);
945 if (
CapProdI(var_increase, var_coeff) >= root_coeff) {
947 return ReportConflictingCycle();
953 ++num_failed_cycles_;
957 if (id_to_count[
id] == 0)
continue;
962 const ConstraintInfo& info = infos_[id];
963 const auto coeffs = GetCoeffs(info);
964 const auto vars = GetVariables(info);
965 IntegerValue var_coeff(0);
966 disassemble_candidates_.clear();
967 ++num_explored_in_disassemble_;
968 time_limit_->AdvanceDeterministicTime(
static_cast<double>(info.rev_size) *
970 for (
int i = 0;
i < info.rev_size; ++
i) {
971 if (vars[
i] == var) {
972 var_coeff = coeffs[
i];
975 const IntegerVariable next_var =
NegationOf(vars[
i]);
976 if (propagated_by_[next_var] ==
id) {
977 disassemble_candidates_.push_back({next_var, coeffs[
i]});
980 propagated_by_[next_var] = -1;
985 for (
const auto [next_var, next_var_coeff] : disassemble_candidates_) {
991 const IntegerValue next_increase =
993 if (next_increase > 0) {
994 disassemble_queue_.push_back({id, next_var, next_increase});
1009void LinearPropagator::AddToQueueIfNeeded(
int id) {
1010 DCHECK_LT(
id, in_queue_.size());
1011 DCHECK_LT(
id, infos_.size());
1013 if (in_queue_[
id])
return;
1015 propagation_queue_.push_back(
id);
1018void LinearPropagator::ClearPropagatedBy() {
1021 for (
const IntegerVariable var :
1022 propagated_by_was_set_.PositionsSetAtLeastOnce()) {
1023 int&
id = propagated_by_[var];
1024 if (
id != -1) --id_to_propagation_count_[id];
1025 propagated_by_[var] = -1;
1027 propagated_by_was_set_.ClearAndResize(propagated_by_was_set_.size());
1028 DCHECK(std::all_of(propagated_by_.begin(), propagated_by_.end(),
1029 [](
int id) { return id == -1; }));
1030 DCHECK(std::all_of(id_to_propagation_count_.begin(),
1031 id_to_propagation_count_.end(),
1032 [](
int count) { return count == 0; }));
void Set(IntegerType index)
void AdvanceDeterministicTime(double deterministic_duration)
void UpdateBound(IntegerVariable var, IntegerValue lb)
bool VarShouldBePushedById(IntegerVariable var, int id)
IntegerValue LowerBound(IntegerVariable i) const
ABSL_MUST_USE_RESULT bool EnqueueWithLazyReason(IntegerLiteral i_lit, int id, IntegerValue propagation_slack, LazyReasonInterface *explainer)
LinearPropagator(Model *model)
~LinearPropagator() override
void SetLevel(int level) final
bool AddConstraint(absl::Span< const Literal > enforcement_literals, absl::Span< const IntegerVariable > vars, absl::Span< const IntegerValue > coeffs, IntegerValue upper_bound)
void Explain(int id, IntegerLiteral to_explain, IntegerReason *reason) final
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
IntegerVariable PositiveVariable(IntegerVariable i)
@ CAN_PROPAGATE_ENFORCEMENT
bool VariableIsPositive(IntegerVariable i)
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
absl::Span< const IntegerValue > coeffs
absl::Span< const Literal > boolean_literals_at_true
absl::Span< const IntegerVariable > vars