26#include "absl/base/log_severity.h"
27#include "absl/cleanup/cleanup.h"
28#include "absl/container/flat_hash_map.h"
29#include "absl/container/flat_hash_set.h"
30#include "absl/container/inlined_vector.h"
31#include "absl/log/check.h"
32#include "absl/numeric/int128.h"
33#include "absl/strings/str_cat.h"
34#include "absl/types/span.h"
58 os <<
"CANNOT_PROPAGATE";
61 os <<
"CAN_PROPAGATE";
88 if (
literal.Index() >=
static_cast<int>(watcher_.size()))
continue;
91 auto& watch_list = watcher_[
literal.Index()];
92 for (
const EnforcementId
id : watch_list) {
96 watch_list[new_size++] = id;
103 watch_list.
resize(new_size);
106 for (
const EnforcementId
id : watcher_[
literal.NegatedIndex()]) {
110 rev_stack_size_ =
static_cast<int>(untrail_stack_.size());
116 const int size =
static_cast<int>(untrail_stack_.size());
117 for (
int i =
size - 1;
i >= rev_stack_size_; --
i) {
118 const auto [id,
status] = untrail_stack_[
i];
120 if (callbacks_[
id] !=
nullptr) callbacks_[id](id,
status);
122 untrail_stack_.resize(rev_stack_size_);
132 absl::Span<const Literal> enforcement,
136 bool is_always_false =
false;
137 temp_literals_.clear();
139 for (
const Literal l : enforcement) {
141 const int size = std::max(l.Index().value(), l.NegatedIndex().value()) + 1;
142 if (
size >
static_cast<int>(watcher_.size())) {
146 if (level == 0 || trail_.
Info(l.Variable()).
level == 0)
continue;
149 if (level == 0 || trail_.
Info(l.Variable()).
level == 0) {
150 is_always_false =
true;
155 temp_literals_.push_back(l);
160 if (is_always_false) {
163 return EnforcementId(-1);
165 if (temp_literals_.empty()) {
168 return EnforcementId(-1);
171 const EnforcementId id(
static_cast<int>(callbacks_.size()));
174 CHECK(!temp_literals_.empty());
175 buffer_.insert(buffer_.end(), temp_literals_.begin(), temp_literals_.end());
179 statuses_.
push_back(temp_literals_.size() == 1
183 if (temp_literals_.size() == 1) {
184 watcher_[temp_literals_[0].Index()].
push_back(
id);
187 const auto span = GetSpan(
id);
188 int num_not_true = 0;
189 for (
int i = 0;
i < span.size(); ++
i) {
191 std::swap(span[num_not_true], span[
i]);
193 if (num_not_true == 2)
break;
197 if (num_not_true == 1) {
198 int max_level = trail_.
Info(span[1].Variable()).
level;
199 for (
int i = 2;
i < span.size(); ++
i) {
200 const int level = trail_.
Info(span[
i].Variable()).
level;
201 if (level > max_level) {
203 std::swap(span[1], span[
i]);
216 }
else if (num_true == temp_literals_.size()) {
218 }
else if (num_true + 1 == temp_literals_.size()) {
221 if (temp_literals_.size() == 1) {
222 if (callbacks_[
id] !=
nullptr) {
232 EnforcementId
id, std::vector<Literal>* reason)
const {
233 for (
const Literal l : GetSpan(
id)) {
234 reason->push_back(l.Negated());
241 EnforcementId
id, absl::Span<const Literal> literal_reason,
242 absl::Span<const IntegerLiteral> integer_reason) {
243 temp_reason_.clear();
245 for (
const Literal l : GetSpan(
id)) {
248 temp_reason_.push_back(l.Negated());
252 unique_unassigned = l.Index();
255 temp_reason_.insert(temp_reason_.end(), literal_reason.begin(),
256 literal_reason.end());
258 return integer_trail_->
ReportConflict(temp_reason_, integer_reason);
264 temp_reason_, integer_reason);
268absl::Span<Literal> EnforcementPropagator::GetSpan(EnforcementId
id) {
269 if (
id < 0)
return {};
270 DCHECK_LE(
id + 1, starts_.size());
271 const int size = starts_[
id + 1] - starts_[id];
273 return absl::MakeSpan(&buffer_[starts_[
id]],
size);
276absl::Span<const Literal> EnforcementPropagator::GetSpan(
277 EnforcementId
id)
const {
278 if (
id < 0)
return {};
279 DCHECK_LE(
id + 1, starts_.size());
280 const int size = starts_[
id + 1] - starts_[id];
282 return absl::MakeSpan(&buffer_[starts_[
id]],
size);
285LiteralIndex EnforcementPropagator::ProcessIdOnTrue(Literal watched,
290 const auto span = GetSpan(
id);
291 if (span.size() == 1) {
297 const int watched_pos = (span[0] == watched) ? 0 : 1;
298 CHECK_EQ(span[watched_pos], watched);
304 for (
int i = 2;
i < span.size(); ++
i) {
305 const Literal l = span[
i];
313 std::swap(span[watched_pos], span[
i]);
314 return span[watched_pos].Index();
331void EnforcementPropagator::ChangeStatus(EnforcementId
id,
334 if (old_status == new_status)
return;
336 untrail_stack_.push_back({id, old_status});
338 statuses_[id] = new_status;
339 if (callbacks_[
id] !=
nullptr) callbacks_[id](id, new_status);
346 for (
const Literal l : GetSpan(
id)) {
352 const int size = GetSpan(
id).size();
365 rev_integer_value_repository_(
370 watcher_id_(watcher_->Register(this)),
371 order_(random_, [this](int id) {
return GetVariables(infos_[
id]); }) {
373 integer_trail_->RegisterWatcher(&modified_vars_);
374 integer_trail_->RegisterReversibleClass(
this);
379 watcher_->SetPropagatorPriority(watcher_id_, 0);
383 if (!VLOG_IS_ON(1))
return;
384 if (shared_stats_ ==
nullptr)
return;
385 std::vector<std::pair<std::string, int64_t>> stats;
386 stats.push_back({
"linear_propag/num_pushes", num_pushes_});
388 {
"linear_propag/num_enforcement_pushes", num_enforcement_pushes_});
390 stats.push_back({
"linear_propag/num_cycles", num_cycles_});
391 stats.push_back({
"linear_propag/num_failed_cycles", num_failed_cycles_});
393 stats.push_back({
"linear_propag/num_short_reasons_", num_short_reasons_});
394 stats.push_back({
"linear_propag/num_long_reasons_", num_long_reasons_});
396 stats.push_back({
"linear_propag/num_scanned", num_scanned_});
397 stats.push_back({
"linear_propag/num_explored_in_disassemble",
398 num_explored_in_disassemble_});
399 stats.push_back({
"linear_propag/num_bool_aborts", num_bool_aborts_});
400 stats.push_back({
"linear_propag/num_loop_aborts", num_loop_aborts_});
401 stats.push_back({
"linear_propag/num_ignored", num_ignored_});
402 stats.push_back({
"linear_propag/num_delayed", num_delayed_});
407 if (level < previous_level_) {
414 for (
const int id : propagation_queue_) in_queue_.
Clear(
id);
415 propagation_queue_.clear();
416 for (
int i = rev_unenforced_size_;
i < unenforced_constraints_.size();
418 in_queue_.
Clear(unenforced_constraints_[
i]);
420 unenforced_constraints_.resize(rev_unenforced_size_);
421 }
else if (level > previous_level_) {
422 rev_unenforced_size_ = unenforced_constraints_.size();
423 rev_int_repository_->
SaveState(&rev_unenforced_size_);
430 if (!propagation_queue_.empty() ||
435 previous_level_ = level;
438void LinearPropagator::SetPropagatedBy(IntegerVariable
var,
int id) {
439 int& ref_id = propagated_by_[
var];
440 if (ref_id ==
id)
return;
442 propagated_by_was_set_.
Set(
var);
445 DCHECK_LT(
var, propagated_by_.size());
447 DCHECK_GE(ref_id, 0);
448 DCHECK_LT(ref_id, id_to_propagation_count_.size());
449 id_to_propagation_count_[ref_id]--;
452 if (
id != -1) id_to_propagation_count_[id]++;
455void LinearPropagator::OnVariableChange(IntegerVariable
var, IntegerValue lb,
458 const int size = var_to_constraint_ids_[
var].size();
459 if (
size == 0)
return;
461 SetPropagatedBy(
var,
id);
463 Bitset64<int>::View in_queue = in_queue_.
view();
465 for (
const int id : var_to_constraint_ids_[
var]) {
466 if (in_queue[
id])
continue;
468 propagation_queue_.push_back(
id);
478 if (
var >= var_to_constraint_ids_.size())
continue;
483 num_terms_for_dtime_update_ = 0;
484 const auto cleanup = ::absl::MakeCleanup([
this]() {
486 static_cast<double>(num_terms_for_dtime_update_) * 1e-9);
521 const int saved_index = trail_->
Index();
522 while (!propagation_queue_.empty()) {
523 const int id = propagation_queue_.front();
524 propagation_queue_.pop_front();
526 const auto [slack, num_to_push] = AnalyzeConstraint(
id);
530 if (!PropagateInfeasibleConstraint(
id, slack))
return false;
537 if (trail_->
Index() > saved_index) {
541 }
else if (num_to_push > 0) {
544 const auto vars = GetVariables(infos_[
id]);
545 const auto coeffs = GetCoeffs(infos_[
id]);
546 for (
int i = 0;
i < num_to_push; ++
i) {
547 const IntegerVariable
var = vars[
i];
548 const IntegerValue coeff = coeffs[
i];
549 const IntegerValue div = slack / coeff;
550 const IntegerValue new_ub = integer_trail_->
LowerBound(
var) + div;
556 const int next_id = order_.
NextId();
557 if (next_id == -1)
break;
562 if (!PropagateOneConstraint(next_id))
return false;
567 if (trail_->
Index() > saved_index) {
577 absl::Span<const Literal> enforcement_literals,
578 absl::Span<const IntegerVariable> vars,
579 absl::Span<const IntegerValue> coeffs, IntegerValue
upper_bound) {
580 if (vars.empty())
return true;
582 for (
const Literal l : enforcement_literals) {
589 CHECK_LT(vars.size(), 1 << 29);
590 if (vars.size() > max_variations_.size()) {
591 max_variations_.resize(vars.size(), 0);
592 buffer_of_ones_.resize(vars.size(), IntegerValue(1));
596 CHECK_EQ(vars.size(), coeffs.size());
597 const int id = infos_.size();
600 info.all_coeffs_are_one =
false;
601 info.start = variables_buffer_.size();
602 info.initial_size = vars.size();
604 info.rev_size = vars.size();
605 infos_.push_back(std::move(info));
609 id_to_propagation_count_.push_back(0);
610 variables_buffer_.insert(variables_buffer_.end(), vars.begin(), vars.end());
611 coeffs_buffer_.insert(coeffs_buffer_.end(), coeffs.begin(), coeffs.end());
612 CanonicalizeConstraint(
id);
614 bool all_at_one =
true;
615 for (
const IntegerValue coeff : GetCoeffs(infos_.back())) {
624 infos_.back().all_coeffs_are_one =
true;
631 if (!enforcement_literals.empty()) {
632 infos_.back().enf_status =
634 infos_.back().enf_id = enforcement_propagator_->
Register(
635 enforcement_literals,
637 infos_[id].enf_status =
static_cast<int>(
status);
643 AddToQueueIfNeeded(
id);
652 const auto info = infos_[id];
653 if (info.initial_size == 2 && info.all_coeffs_are_one) {
654 const auto vars = GetVariables(info);
657 vars[0], vars[1], initial_rhs_[
id]);
664 AddToQueueIfNeeded(
id);
665 infos_.back().enf_id = -1;
669 order_.
Resize(var_to_constraint_ids_.size(), in_queue_.
size());
670 for (
const IntegerVariable
var : GetVariables(infos_[
id])) {
672 if (
var >= var_to_constraint_ids_.size()) {
677 propagated_by_was_set_.
Resize(IntegerVariable(
size));
690 if (!is_watched_[
var]) {
691 is_watched_[
var] =
true;
698 num_terms_for_dtime_update_ = 0;
699 const auto cleanup = ::absl::MakeCleanup([
this]() {
701 static_cast<double>(num_terms_for_dtime_update_) * 1e-9);
703 if (!PropagateOneConstraint(
id))
return false;
707absl::Span<IntegerValue> LinearPropagator::GetCoeffs(
708 const ConstraintInfo& info) {
709 if (info.all_coeffs_are_one) {
710 return absl::MakeSpan(&buffer_of_ones_[0], info.initial_size);
712 return absl::MakeSpan(&coeffs_buffer_[info.start], info.initial_size);
715absl::Span<IntegerVariable> LinearPropagator::GetVariables(
716 const ConstraintInfo& info) {
717 return absl::MakeSpan(&variables_buffer_[info.start], info.initial_size);
720void LinearPropagator::CanonicalizeConstraint(
int id) {
721 const ConstraintInfo& info = infos_[id];
722 const auto coeffs = GetCoeffs(info);
723 const auto vars = GetVariables(info);
724 for (
int i = 0;
i < vars.size(); ++
i) {
726 coeffs[
i] = -coeffs[
i];
734 absl::flat_hash_set<IntegerVariable> no_dup;
735 for (
const IntegerVariable
var : GetVariables(info)) {
743std::pair<IntegerValue, int> LinearPropagator::AnalyzeConstraint(
int id) {
747 ConstraintInfo& info = infos_[id];
752 if (enf_status != debug_status) {
759 LOG(FATAL) <<
"Enforcement status not up to date: " << enf_status
760 <<
" vs debug: " << debug_status;
767 DCHECK(!in_queue_[
id]);
772 unenforced_constraints_.push_back(
id);
780 IntegerValue implied_lb(0);
781 const auto vars = GetVariables(info);
782 IntegerValue max_variation(0);
783 bool first_change =
true;
784 num_terms_for_dtime_update_ += info.rev_size;
785 IntegerValue* max_variations = max_variations_.data();
787 if (info.all_coeffs_are_one) {
789 for (
int i = 0;
i < info.rev_size;) {
790 const IntegerVariable
var = vars[
i];
797 rev_int_repository_->
SaveState(&info.rev_size);
798 rev_integer_value_repository_->
SaveState(&info.rev_rhs);
799 first_change =
false;
802 std::swap(vars[
i], vars[info.rev_size]);
806 max_variations[
i] = diff;
807 max_variation = std::max(max_variation, diff);
812 const auto coeffs = GetCoeffs(info);
813 for (
int i = 0;
i < info.rev_size;) {
814 const IntegerVariable
var = vars[
i];
815 const IntegerValue coeff = coeffs[
i];
822 rev_int_repository_->
SaveState(&info.rev_size);
823 rev_integer_value_repository_->
SaveState(&info.rev_rhs);
824 first_change =
false;
827 std::swap(vars[
i], vars[info.rev_size]);
828 std::swap(coeffs[
i], coeffs[info.rev_size]);
829 info.rev_rhs -= coeff * lb;
831 implied_lb += coeff * lb;
832 max_variations[
i] = diff * coeff;
833 max_variation = std::max(max_variation, max_variations[
i]);
841 const IntegerValue slack = info.rev_rhs - implied_lb;
846 if (slack < 0 || max_variation <= slack)
return {slack, 0};
850 const auto coeffs = GetCoeffs(info);
851 for (
int i = 0;
i < info.rev_size; ++
i) {
852 if (max_variations[
i] <= slack)
continue;
853 std::swap(vars[
i], vars[num_to_push]);
854 std::swap(coeffs[
i], coeffs[num_to_push]);
857 return {slack, num_to_push};
862bool LinearPropagator::PropagateInfeasibleConstraint(
int id,
863 IntegerValue slack) {
865 const ConstraintInfo& info = infos_[id];
866 const auto vars = GetVariables(info);
867 const auto coeffs = GetCoeffs(info);
870 integer_reason_.clear();
871 reason_coeffs_.clear();
872 for (
int i = 0;
i < info.initial_size; ++
i) {
873 const IntegerVariable
var = vars[
i];
876 reason_coeffs_.push_back(coeffs[
i]);
883 ++num_enforcement_pushes_;
889 IntegerVariable var_to_explain,
int trail_index,
890 std::vector<Literal>* literals_reason,
891 std::vector<int>* trail_indices_reason) {
892 literals_reason->clear();
893 trail_indices_reason->clear();
894 const ConstraintInfo& info = infos_[id];
896 reason_coeffs_.clear();
898 const auto coeffs = GetCoeffs(info);
899 const auto vars = GetVariables(info);
900 for (
int i = 0;
i < info.initial_size; ++
i) {
901 const IntegerVariable
var = vars[
i];
908 trail_indices_reason->push_back(
index);
909 if (propagation_slack > 0) {
910 reason_coeffs_.push_back(coeffs[
i]);
914 if (propagation_slack > 0) {
916 trail_indices_reason);
920bool LinearPropagator::PropagateOneConstraint(
int id) {
921 const auto [slack, num_to_push] = AnalyzeConstraint(
id);
922 if (slack < 0)
return PropagateInfeasibleConstraint(
id, slack);
923 if (num_to_push == 0)
return true;
925 DCHECK_GT(num_to_push, 0);
927 const ConstraintInfo& info = infos_[id];
928 const auto vars = GetVariables(info);
929 const auto coeffs = GetCoeffs(info);
939 if (!DisassembleSubtree(
id, num_to_push)) {
946 for (
int i = 0;
i < num_to_push; ++
i) {
955 const IntegerVariable
var = vars[
i];
956 const IntegerValue coeff = coeffs[
i];
957 const IntegerValue div = slack / coeff;
958 const IntegerValue new_ub = integer_trail_->
LowerBound(
var) + div;
959 const IntegerValue propagation_slack = (div + 1) * coeff - slack - 1;
967 const IntegerValue actual_ub = integer_trail_->
UpperBound(
var);
969 if (actual_ub < new_ub) {
971 OnVariableChange(next_var, -actual_ub, -1);
972 }
else if (actual_ub == new_ub) {
973 OnVariableChange(next_var, -actual_ub,
id);
976 std::swap(vars[
i], vars[num_pushed]);
977 std::swap(coeffs[
i], coeffs[num_pushed]);
988std::string LinearPropagator::ConstraintDebugString(
int id) {
990 const ConstraintInfo& info = infos_[id];
991 const auto coeffs = GetCoeffs(info);
992 const auto vars = GetVariables(info);
993 IntegerValue implied_lb(0);
994 IntegerValue rhs_correction(0);
995 for (
int i = 0;
i < info.initial_size; ++
i) {
996 const IntegerValue term = coeffs[
i] * integer_trail_->
LowerBound(vars[
i]);
997 if (
i >= info.rev_size) {
998 rhs_correction += term;
1001 absl::StrAppend(&result,
" +", coeffs[
i].
value(),
"*X", vars[
i].
value());
1003 const IntegerValue original_rhs = info.rev_rhs + rhs_correction;
1004 absl::StrAppend(&result,
" <= ", original_rhs.value(),
1005 " slack=", original_rhs.value() - implied_lb.value());
1006 absl::StrAppend(&result,
" enf=", info.enf_status);
1010bool LinearPropagator::ReportConflictingCycle() {
1018 literal_reason_.clear();
1019 integer_reason_.clear();
1020 absl::int128 rhs_sum = 0;
1021 absl::flat_hash_map<IntegerVariable, absl::int128> map_sum;
1022 for (
const auto [
id, next_var, increase] : disassemble_branch_) {
1023 const ConstraintInfo& info = infos_[id];
1026 const auto coeffs = GetCoeffs(info);
1027 const auto vars = GetVariables(info);
1028 IntegerValue rhs_correction(0);
1029 for (
int i = 0;
i < info.initial_size; ++
i) {
1030 if (
i >= info.rev_size) {
1031 rhs_correction += coeffs[
i] * integer_trail_->
LowerBound(vars[
i]);
1034 map_sum[vars[
i]] += coeffs[
i].value();
1039 rhs_sum += (info.rev_rhs + rhs_correction).
value();
1044 absl::int128 implied_lb = 0;
1045 for (
const auto [
var, coeff] : map_sum) {
1051 coeff * absl::int128{integer_trail_->
LowerBound(
var).value()};
1052 }
else if (coeff < 0) {
1058 coeff * absl::int128{integer_trail_->
UpperBound(
var).value()};
1061 if (implied_lb > rhs_sum) {
1063 std::sort(integer_reason_.begin(), integer_reason_.end(),
1064 [](
const IntegerLiteral&
a,
const IntegerLiteral&
b) {
1065 return a.var < b.var;
1069 const absl::int128 limit{std::numeric_limits<int64_t>::max()};
1070 const absl::int128 slack = implied_lb - rhs_sum;
1072 reason_coeffs_.clear();
1074 for (
const IntegerLiteral i_lit : integer_reason_) {
1081 reason_coeffs_.push_back(
static_cast<int64_t
>(c));
1084 const IntegerValue slack64(
1085 static_cast<int64_t
>(std::min(limit, slack)));
1091 ++num_short_reasons_;
1092 VLOG(2) <<
"Simplified " << integer_reason_.size() <<
" slack "
1093 << implied_lb - rhs_sum;
1094 return integer_trail_->
ReportConflict(literal_reason_, integer_reason_);
1103 literal_reason_.clear();
1104 integer_reason_.clear();
1106 for (
const auto [
id, next_var, increase] : disassemble_branch_) {
1107 const ConstraintInfo& info = infos_[id];
1110 for (
const IntegerVariable
var : GetVariables(infos_[
id])) {
1113 if (
var == previous_var)
continue;
1122 previous_var = next_var;
1124 VLOG(2) << next_var <<
" [" << integer_trail_->
LowerBound(next_var) <<
","
1126 <<
"] : " << ConstraintDebugString(
id);
1128 ++num_long_reasons_;
1129 return integer_trail_->
ReportConflict(literal_reason_, integer_reason_);
1132std::pair<IntegerValue, IntegerValue> LinearPropagator::GetCycleCoefficients(
1133 int id, IntegerVariable
var, IntegerVariable next_var) {
1134 const ConstraintInfo& info = infos_[id];
1135 const auto coeffs = GetCoeffs(info);
1136 const auto vars = GetVariables(info);
1137 IntegerValue var_coeff(0);
1138 IntegerValue next_coeff(0);
1139 for (
int i = 0;
i < info.initial_size; ++
i) {
1140 if (vars[
i] ==
var) var_coeff = coeffs[
i];
1141 if (vars[
i] ==
NegationOf(next_var)) next_coeff = coeffs[
i];
1143 DCHECK_NE(var_coeff, 0);
1144 DCHECK_NE(next_coeff, 0);
1145 return {var_coeff, next_coeff};
1157bool LinearPropagator::DisassembleSubtree(
int root_id,
int num_tight) {
1163 disassemble_queue_.clear();
1164 disassemble_branch_.clear();
1166 const ConstraintInfo& info = infos_[root_id];
1167 const auto vars = GetVariables(info);
1168 for (
int i = 0;
i < num_tight; ++
i) {
1169 disassemble_queue_.push_back({root_id,
NegationOf(vars[
i]), 1});
1175 absl::Span<int> id_to_count = absl::MakeSpan(id_to_propagation_count_);
1176 while (!disassemble_queue_.empty()) {
1177 const auto [prev_id,
var, increase] = disassemble_queue_.back();
1178 if (!disassemble_branch_.empty() &&
1179 disassemble_branch_.back().id == prev_id &&
1180 disassemble_branch_.back().var ==
var) {
1181 disassemble_branch_.pop_back();
1182 disassemble_queue_.pop_back();
1186 disassemble_branch_.push_back({prev_id,
var, increase});
1189 static_cast<double>(var_to_constraint_ids_[
var].
size()) * 1e-9);
1190 for (
const int id : var_to_constraint_ids_[
var]) {
1191 if (
id == root_id) {
1195 CHECK(!disassemble_branch_.empty());
1198 const auto [test_id, root_var, var_increase] = disassemble_branch_[0];
1199 CHECK_EQ(test_id, root_id);
1200 CHECK_NE(
var, root_var);
1205 const auto [var_coeff, root_coeff] =
1206 GetCycleCoefficients(
id,
var, root_var);
1207 if (
CapProdI(var_increase, var_coeff) >= root_coeff) {
1209 return ReportConflictingCycle();
1215 ++num_failed_cycles_;
1219 if (id_to_count[
id] == 0)
continue;
1224 const ConstraintInfo& info = infos_[id];
1225 const auto coeffs = GetCoeffs(info);
1226 const auto vars = GetVariables(info);
1227 IntegerValue var_coeff(0);
1228 disassemble_candidates_.clear();
1229 ++num_explored_in_disassemble_;
1232 for (
int i = 0;
i < info.rev_size; ++
i) {
1233 if (vars[
i] ==
var) {
1234 var_coeff = coeffs[
i];
1237 const IntegerVariable next_var =
NegationOf(vars[
i]);
1238 if (propagated_by_[next_var] ==
id) {
1239 disassemble_candidates_.push_back({next_var, coeffs[
i]});
1242 propagated_by_[next_var] = -1;
1247 for (
const auto [next_var, next_var_coeff] : disassemble_candidates_) {
1253 const IntegerValue next_increase =
1255 if (next_increase > 0) {
1256 disassemble_queue_.push_back({id, next_var, next_increase});
1271void LinearPropagator::AddToQueueIfNeeded(
int id) {
1272 DCHECK_LT(
id, in_queue_.
size());
1273 DCHECK_LT(
id, infos_.size());
1275 if (in_queue_[
id])
return;
1277 propagation_queue_.push_back(
id);
1280void LinearPropagator::ClearPropagatedBy() {
1283 for (
const IntegerVariable
var :
1285 int&
id = propagated_by_[
var];
1286 if (
id != -1) --id_to_propagation_count_[id];
1287 propagated_by_[
var] = -1;
1290 DCHECK(std::all_of(propagated_by_.begin(), propagated_by_.end(),
1291 [](
int id) { return id == -1; }));
1292 DCHECK(std::all_of(id_to_propagation_count_.begin(),
1293 id_to_propagation_count_.end(),
1294 [](
int count) { return count == 0; }));
IndexType size() const
Returns how many bits this Bitset64 can hold.
void Clear(IndexType i)
Sets the bit at position i to 0.
void resize(int size)
Resizes the Bitset64 to the given number of bits. New bits are sets to 0.
void Set(IndexType i)
Sets the bit at position i to 1.
void SaveStateWithStamp(T *object, int64_t *stamp)
void SaveState(T *object)
void ClearAndResize(IntegerType size)
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
void Set(IntegerType index)
void Resize(IntegerType size)
void AdvanceDeterministicTime(double deterministic_duration)
void UpdateBound(IntegerVariable var, IntegerValue lb)
bool VarShouldBePushedById(IntegerVariable var, int id)
void Resize(int num_vars, int num_ids)
void Register(int id, IntegerVariable var, IntegerValue lb)
This is meant as an helper to deal with enforcement for any constraint.
EnforcementPropagator(Model *model)
bool Propagate(Trail *trail) final
SatPropagator interface.
void AddEnforcementReason(EnforcementId id, std::vector< Literal > *reason) const
Add the enforcement reason to the given vector.
ABSL_MUST_USE_RESULT bool PropagateWhenFalse(EnforcementId id, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
void Untrail(const Trail &trail, int trail_index) final
EnforcementId Register(absl::Span< const Literal > enforcement, std::function< void(EnforcementId, EnforcementStatus)> callback=nullptr)
absl::Span< const Literal > GetEnforcementLiterals(EnforcementId id) const
Returns the enforcement literals of the given id.
EnforcementStatus DebugStatus(EnforcementId id)
void WatchLowerBound(IntegerVariable var, int id, int watch_index=-1)
void CallOnNextPropagate(int id)
Add the given propagator to its queue.
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
int FindTrailIndexOfVarBefore(IntegerVariable var, int threshold) const
bool VariableLowerBoundIsFromLevelZero(IntegerVariable var) const
Returns true if the variable lower bound is still the one from level zero.
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
void RelaxLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, std::vector< IntegerLiteral > *reason) const
IntegerValue UpperBound(IntegerVariable i) const
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
ABSL_MUST_USE_RESULT bool EnqueueWithLazyReason(IntegerLiteral i_lit, int id, IntegerValue propagation_slack, LazyReasonInterface *explainer)
Lazy reason API.
IntegerVariable NumIntegerVariables() const
const IntegerValue * LowerBoundsData() const
void EnqueueLiteral(Literal literal, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
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)
Adds a new constraint to the propagator.
void Explain(int id, IntegerValue propagation_slack, IntegerVariable var_to_explain, int trail_index, std::vector< Literal > *literals_reason, std::vector< int > *trail_indices_reason) final
For LazyReasonInterface.
void PushConditionalRelation(absl::Span< const Literal > enforcements, IntegerVariable a, IntegerVariable b, IntegerValue rhs)
Base class for all the SAT constraints.
int propagation_trail_index_
bool PropagationIsDone(const Trail &trail) const
Returns true iff all the trail was inspected by this propagator.
void AddPropagator(SatPropagator *propagator)
Simple class to add statistics by name and print them at the end.
void AddStats(absl::Span< const std::pair< std::string, int64_t > > stats)
Adds a bunch of stats, adding count for the same key together.
int CurrentDecisionLevel() const
const AssignmentInfo & Info(BooleanVariable var) const
const VariablesAssignment & Assignment() const
bool LiteralIsAssigned(Literal literal) const
bool LiteralIsFalse(Literal literal) const
bool LiteralIsTrue(Literal literal) const
void push_back(const value_type &val)
void resize(size_type new_size)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
const LiteralIndex kNoLiteralIndex(-1)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Returns the vector of the negated variables.
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
IntegerVariable PositiveVariable(IntegerVariable i)
std::ostream & operator<<(std::ostream &os, const BoolVar &var)
@ IS_ENFORCED
All enforcement literals are true.
@ CANNOT_PROPAGATE
More than two literals are unassigned.
@ IS_FALSE
One enforcement literal is false.
@ CAN_PROPAGATE
All enforcement literals are true but one.
bool VariableIsPositive(IntegerVariable i)
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
Overflows and saturated arithmetic.
In SWIG mode, we don't want anything besides these top-level includes.
std::vector< double > lower_bounds
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)