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"
59 os <<
"CANNOT_PROPAGATE";
62 os <<
"CAN_PROPAGATE";
73 trail_(*model->GetOrCreate<
Trail>()),
86 rev_int_repository_->SaveStateWithStamp(&rev_stack_size_, &rev_stamp_);
89 if (literal.
Index() >=
static_cast<int>(watcher_.size()))
continue;
92 auto& watch_list = watcher_[literal.
Index()];
93 for (
const EnforcementId
id : watch_list) {
94 const LiteralIndex index = ProcessIdOnTrue(literal,
id);
97 watch_list[new_size++] = id;
100 CHECK_NE(index, literal.
Index());
101 watcher_[index].push_back(
id);
104 watch_list.resize(new_size);
107 for (
const EnforcementId
id : watcher_[literal.
NegatedIndex()]) {
111 rev_stack_size_ =
static_cast<int>(untrail_stack_.size());
115 for (
const EnforcementId
id : ids_to_fix_until_next_root_level_) {
118 if (trail_.CurrentDecisionLevel() == 0) {
119 ids_to_fix_until_next_root_level_.clear();
127 const int size =
static_cast<int>(untrail_stack_.size());
128 for (
int i = size - 1;
i >= rev_stack_size_; --
i) {
129 const auto [id, status] = untrail_stack_[
i];
130 statuses_[id] = status;
131 if (callbacks_[
id] !=
nullptr) callbacks_[id](id, status);
133 untrail_stack_.resize(rev_stack_size_);
143 absl::Span<const Literal> enforcement,
147 bool is_always_false =
false;
148 temp_literals_.clear();
149 const int level = trail_.CurrentDecisionLevel();
150 for (
const Literal l : enforcement) {
152 const int size = std::max(l.Index().value(), l.NegatedIndex().value()) + 1;
153 if (size >
static_cast<int>(watcher_.size())) {
154 watcher_.resize(size);
156 if (assignment_.LiteralIsTrue(l)) {
157 if (level == 0 || trail_.Info(l.Variable()).level == 0)
continue;
159 }
else if (assignment_.LiteralIsFalse(l)) {
160 if (level == 0 || trail_.Info(l.Variable()).level == 0) {
161 is_always_false =
true;
166 temp_literals_.push_back(l);
171 if (is_always_false) {
172 if (callback !=
nullptr)
174 return EnforcementId(-1);
176 if (temp_literals_.empty()) {
177 if (callback !=
nullptr)
179 return EnforcementId(-1);
182 const EnforcementId id(
static_cast<int>(callbacks_.size()));
183 callbacks_.push_back(std::move(callback));
185 CHECK(!temp_literals_.empty());
186 buffer_.insert(buffer_.end(), temp_literals_.begin(), temp_literals_.end());
187 starts_.push_back(buffer_.size());
190 statuses_.push_back(temp_literals_.size() == 1
194 if (temp_literals_.size() == 1) {
195 watcher_[temp_literals_[0].Index()].push_back(
id);
198 const auto span = GetSpan(
id);
199 int num_not_true = 0;
200 for (
int i = 0;
i < span.size(); ++
i) {
201 if (assignment_.LiteralIsTrue(span[
i]))
continue;
202 std::swap(span[num_not_true], span[
i]);
204 if (num_not_true == 2)
break;
208 if (num_not_true == 1) {
209 int max_level = trail_.Info(span[1].
Variable()).level;
210 for (
int i = 2;
i < span.size(); ++
i) {
211 const int level = trail_.Info(span[
i].
Variable()).level;
212 if (level > max_level) {
214 std::swap(span[1], span[
i]);
219 watcher_[span[0].Index()].push_back(
id);
220 watcher_[span[1].Index()].push_back(
id);
227 }
else if (num_true == temp_literals_.size()) {
229 }
else if (num_true + 1 == temp_literals_.size()) {
232 if (temp_literals_.size() == 1) {
233 if (callbacks_[
id] !=
nullptr) {
241 if (trail_.CurrentDecisionLevel() > 0 &&
243 ids_to_fix_until_next_root_level_.push_back(
id);
251 EnforcementId
id, std::vector<Literal>* reason)
const {
252 for (
const Literal l : GetSpan(
id)) {
253 reason->push_back(l.Negated());
260 EnforcementId
id, absl::Span<const Literal> literal_reason,
261 absl::Span<const IntegerLiteral> integer_reason) {
262 temp_reason_.clear();
264 for (
const Literal l : GetSpan(
id)) {
265 if (assignment_.LiteralIsFalse(l))
return true;
266 if (assignment_.LiteralIsTrue(l)) {
267 temp_reason_.push_back(l.Negated());
271 unique_unassigned = l.Index();
274 temp_reason_.insert(temp_reason_.end(), literal_reason.begin(),
275 literal_reason.end());
277 return integer_trail_->ReportConflict(temp_reason_, integer_reason);
282 integer_trail_->EnqueueLiteral(
Literal(unique_unassigned).Negated(),
283 temp_reason_, integer_reason);
287absl::Span<Literal> EnforcementPropagator::GetSpan(EnforcementId
id) {
288 if (
id < 0)
return {};
289 DCHECK_LE(
id + 1, starts_.size());
290 const int size = starts_[
id + 1] - starts_[id];
292 return absl::MakeSpan(&buffer_[starts_[
id]], size);
295absl::Span<const Literal> EnforcementPropagator::GetSpan(
296 EnforcementId
id)
const {
297 if (
id < 0)
return {};
298 DCHECK_LE(
id + 1, starts_.size());
299 const int size = starts_[
id + 1] - starts_[id];
301 return absl::MakeSpan(&buffer_[starts_[
id]], size);
304LiteralIndex EnforcementPropagator::ProcessIdOnTrue(
Literal watched,
309 const auto span = GetSpan(
id);
310 if (span.size() == 1) {
316 const int watched_pos = (span[0] == watched) ? 0 : 1;
317 CHECK_EQ(span[watched_pos], watched);
318 if (assignment_.LiteralIsFalse(span[watched_pos ^ 1])) {
323 for (
int i = 2;
i < span.size(); ++
i) {
324 const Literal l = span[
i];
325 if (assignment_.LiteralIsFalse(l)) {
329 if (!assignment_.LiteralIsAssigned(l)) {
332 std::swap(span[watched_pos], span[
i]);
333 return span[watched_pos].Index();
338 if (assignment_.LiteralIsTrue(span[watched_pos ^ 1])) {
350void EnforcementPropagator::ChangeStatus(EnforcementId
id,
353 if (old_status == new_status)
return;
354 if (trail_.CurrentDecisionLevel() != 0) {
355 untrail_stack_.push_back({id, old_status});
357 statuses_[id] = new_status;
358 if (callbacks_[
id] !=
nullptr) callbacks_[id](id, new_status);
365 for (
const Literal l : GetSpan(
id)) {
366 if (assignment_.LiteralIsFalse(l)) {
369 if (assignment_.LiteralIsTrue(l)) ++num_true;
371 const int size = GetSpan(
id).size();
378 : trail_(model->GetOrCreate<
Trail>()),
382 time_limit_(model->GetOrCreate<
TimeLimit>()),
384 rev_integer_value_repository_(
389 watcher_id_(watcher_->Register(this)),
390 order_(random_, [this](int id) {
return GetVariables(infos_[
id]); }) {
392 integer_trail_->RegisterWatcher(&modified_vars_);
393 integer_trail_->RegisterReversibleClass(
this);
398 watcher_->SetPropagatorPriority(watcher_id_, 0);
402 if (!VLOG_IS_ON(1))
return;
403 if (shared_stats_ ==
nullptr)
return;
404 std::vector<std::pair<std::string, int64_t>> stats;
405 stats.push_back({
"linear_propag/num_pushes", num_pushes_});
407 {
"linear_propag/num_enforcement_pushes", num_enforcement_pushes_});
409 stats.push_back({
"linear_propag/num_cycles", num_cycles_});
410 stats.push_back({
"linear_propag/num_failed_cycles", num_failed_cycles_});
412 stats.push_back({
"linear_propag/num_short_reasons_", num_short_reasons_});
413 stats.push_back({
"linear_propag/num_long_reasons_", num_long_reasons_});
415 stats.push_back({
"linear_propag/num_scanned", num_scanned_});
416 stats.push_back({
"linear_propag/num_explored_in_disassemble",
417 num_explored_in_disassemble_});
418 stats.push_back({
"linear_propag/num_bool_aborts", num_bool_aborts_});
419 stats.push_back({
"linear_propag/num_loop_aborts", num_loop_aborts_});
420 stats.push_back({
"linear_propag/num_ignored", num_ignored_});
421 stats.push_back({
"linear_propag/num_delayed", num_delayed_});
422 shared_stats_->AddStats(stats);
426 if (level < previous_level_) {
428 modified_vars_.ClearAll();
433 for (
const int id : propagation_queue_) in_queue_.Clear(
id);
434 propagation_queue_.clear();
435 for (
int i = rev_unenforced_size_;
i < unenforced_constraints_.size();
437 in_queue_.Clear(unenforced_constraints_[
i]);
439 unenforced_constraints_.resize(rev_unenforced_size_);
440 }
else if (level > previous_level_) {
441 rev_unenforced_size_ = unenforced_constraints_.size();
442 rev_int_repository_->SaveState(&rev_unenforced_size_);
449 if (!propagation_queue_.empty() ||
450 !modified_vars_.PositionsSetAtLeastOnce().empty() || !order_.IsEmpty()) {
451 watcher_->CallOnNextPropagate(watcher_id_);
454 previous_level_ = level;
457void LinearPropagator::SetPropagatedBy(IntegerVariable var,
int id) {
458 int& ref_id = propagated_by_[var];
459 if (ref_id ==
id)
return;
461 propagated_by_was_set_.
Set(var);
464 DCHECK_LT(var, propagated_by_.size());
466 DCHECK_GE(ref_id, 0);
467 DCHECK_LT(ref_id, id_to_propagation_count_.size());
468 id_to_propagation_count_[ref_id]--;
471 if (
id != -1) id_to_propagation_count_[id]++;
474void LinearPropagator::OnVariableChange(IntegerVariable var, IntegerValue lb,
477 const int size = var_to_constraint_ids_[var].size();
478 if (size == 0)
return;
480 SetPropagatedBy(var,
id);
482 Bitset64<int>::View in_queue = in_queue_.
view();
484 for (
const int id : var_to_constraint_ids_[var]) {
485 if (in_queue[
id])
continue;
487 propagation_queue_.push_back(
id);
496 for (
const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
497 if (var >= var_to_constraint_ids_.size())
continue;
498 OnVariableChange(var, integer_trail_->LowerBound(var), -1);
502 num_terms_for_dtime_update_ = 0;
503 const auto cleanup = ::absl::MakeCleanup([
this]() {
504 time_limit_->AdvanceDeterministicTime(
505 static_cast<double>(num_terms_for_dtime_update_) * 1e-9);
506 modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
540 const int saved_index = trail_->Index();
541 while (!propagation_queue_.empty()) {
542 const int id = propagation_queue_.front();
543 propagation_queue_.pop_front();
545 const auto [slack, num_to_push] = AnalyzeConstraint(
id);
549 if (!PropagateInfeasibleConstraint(
id, slack))
return false;
556 if (trail_->Index() > saved_index) {
560 }
else if (num_to_push > 0) {
563 const auto vars = GetVariables(infos_[
id]);
564 const auto coeffs = GetCoeffs(infos_[
id]);
565 for (
int i = 0;
i < num_to_push; ++
i) {
566 const IntegerVariable var = vars[
i];
567 const IntegerValue coeff = coeffs[
i];
568 const IntegerValue div = slack / coeff;
569 const IntegerValue new_ub = integer_trail_->LowerBound(var) + div;
570 order_.Register(
id,
NegationOf(var), -new_ub);
575 const int next_id = order_.NextId();
576 if (next_id == -1)
break;
581 if (!PropagateOneConstraint(next_id))
return false;
586 if (trail_->Index() > saved_index) {
596 absl::Span<const Literal> enforcement_literals,
597 absl::Span<const IntegerVariable> vars,
598 absl::Span<const IntegerValue> coeffs, IntegerValue upper_bound) {
599 if (vars.empty())
return true;
600 if (trail_->CurrentDecisionLevel() == 0) {
601 for (
const Literal l : enforcement_literals) {
602 if (trail_->Assignment().LiteralIsFalse(l))
return true;
608 CHECK_LT(vars.size(), 1 << 29);
609 if (vars.size() > max_variations_.size()) {
610 max_variations_.resize(vars.size(), 0);
611 buffer_of_ones_.resize(vars.size(), IntegerValue(1));
615 CHECK_EQ(vars.size(), coeffs.size());
616 const int id = infos_.size();
619 info.all_coeffs_are_one =
false;
620 info.start = variables_buffer_.size();
621 info.initial_size = vars.size();
622 info.rev_rhs = upper_bound;
623 info.rev_size = vars.size();
624 infos_.push_back(std::move(info));
625 initial_rhs_.push_back(upper_bound);
628 id_to_propagation_count_.push_back(0);
629 variables_buffer_.insert(variables_buffer_.end(), vars.begin(), vars.end());
630 coeffs_buffer_.insert(coeffs_buffer_.end(), coeffs.begin(), coeffs.end());
631 CanonicalizeConstraint(
id);
633 bool all_at_one =
true;
634 for (
const IntegerValue coeff : GetCoeffs(infos_.back())) {
643 infos_.back().all_coeffs_are_one =
true;
648 in_queue_.resize(in_queue_.size() + 1);
650 if (!enforcement_literals.empty()) {
651 infos_.back().enf_status =
653 infos_.back().enf_id = enforcement_propagator_->Register(
654 enforcement_literals,
656 infos_[id].enf_status =
static_cast<int>(status);
662 AddToQueueIfNeeded(
id);
663 watcher_->CallOnNextPropagate(watcher_id_);
671 const auto info = infos_[id];
672 if (info.initial_size == 2 && info.all_coeffs_are_one) {
673 const auto vars = GetVariables(info);
674 precedences_->PushConditionalRelation(
675 enforcement_propagator_->GetEnforcementLiterals(enf_id),
676 vars[0], vars[1], initial_rhs_[
id]);
683 AddToQueueIfNeeded(
id);
684 infos_.back().enf_id = -1;
688 order_.Resize(var_to_constraint_ids_.size(), in_queue_.size());
689 for (
const IntegerVariable var : GetVariables(infos_[
id])) {
691 if (var >= var_to_constraint_ids_.size()) {
693 const int size = std::max(var,
NegationOf(var)).value() + 1;
694 var_to_constraint_ids_.resize(size);
695 propagated_by_.resize(size, -1);
696 propagated_by_was_set_.Resize(IntegerVariable(size));
697 is_watched_.resize(size,
false);
699 order_.Resize(size, in_queue_.size());
705 var_to_constraint_ids_[var].push_back(
id);
709 if (!is_watched_[var]) {
710 is_watched_[var] =
true;
711 watcher_->WatchLowerBound(var, watcher_id_);
717 num_terms_for_dtime_update_ = 0;
718 const auto cleanup = ::absl::MakeCleanup([
this]() {
719 time_limit_->AdvanceDeterministicTime(
720 static_cast<double>(num_terms_for_dtime_update_) * 1e-9);
722 if (!PropagateOneConstraint(
id))
return false;
726absl::Span<IntegerValue> LinearPropagator::GetCoeffs(
727 const ConstraintInfo& info) {
728 if (info.all_coeffs_are_one) {
729 return absl::MakeSpan(&buffer_of_ones_[0], info.initial_size);
731 return absl::MakeSpan(&coeffs_buffer_[info.start], info.initial_size);
734absl::Span<IntegerVariable> LinearPropagator::GetVariables(
735 const ConstraintInfo& info) {
736 return absl::MakeSpan(&variables_buffer_[info.start], info.initial_size);
739void LinearPropagator::CanonicalizeConstraint(
int id) {
740 const ConstraintInfo& info = infos_[id];
741 const auto coeffs = GetCoeffs(info);
742 const auto vars = GetVariables(info);
743 for (
int i = 0;
i < vars.size(); ++
i) {
745 coeffs[
i] = -coeffs[
i];
753 absl::flat_hash_set<IntegerVariable> no_dup;
754 for (
const IntegerVariable var : GetVariables(info)) {
762std::pair<IntegerValue, int> LinearPropagator::AnalyzeConstraint(
int id) {
766 ConstraintInfo& info = infos_[id];
768 if (
DEBUG_MODE && enforcement_propagator_->PropagationIsDone(*trail_)) {
770 enforcement_propagator_->DebugStatus(info.enf_id);
771 if (enf_status != debug_status) {
778 LOG(FATAL) <<
"Enforcement status not up to date: " << enf_status
779 <<
" vs debug: " << debug_status;
786 DCHECK(!in_queue_[
id]);
791 unenforced_constraints_.push_back(
id);
799 IntegerValue implied_lb(0);
800 const auto vars = GetVariables(info);
801 IntegerValue max_variation(0);
802 bool first_change =
true;
803 num_terms_for_dtime_update_ += info.rev_size;
804 IntegerValue* max_variations = max_variations_.data();
805 const IntegerValue* lower_bounds = integer_trail_->LowerBoundsData();
806 if (info.all_coeffs_are_one) {
808 for (
int i = 0;
i < info.rev_size;) {
809 const IntegerVariable var = vars[
i];
810 const IntegerValue lb = lower_bounds[var.value()];
811 const IntegerValue diff = -lower_bounds[
NegationOf(var).value()] - lb;
816 rev_int_repository_->SaveState(&info.rev_size);
817 rev_integer_value_repository_->SaveState(&info.rev_rhs);
818 first_change =
false;
821 std::swap(vars[
i], vars[info.rev_size]);
825 max_variations[
i] = diff;
826 max_variation = std::max(max_variation, diff);
831 const auto coeffs = GetCoeffs(info);
832 for (
int i = 0;
i < info.rev_size;) {
833 const IntegerVariable var = vars[
i];
834 const IntegerValue coeff = coeffs[
i];
835 const IntegerValue lb = lower_bounds[var.value()];
836 const IntegerValue diff = -lower_bounds[
NegationOf(var).value()] - lb;
841 rev_int_repository_->SaveState(&info.rev_size);
842 rev_integer_value_repository_->SaveState(&info.rev_rhs);
843 first_change =
false;
846 std::swap(vars[
i], vars[info.rev_size]);
847 std::swap(coeffs[
i], coeffs[info.rev_size]);
848 info.rev_rhs -= coeff * lb;
850 implied_lb += coeff * lb;
851 max_variations[
i] = diff * coeff;
852 max_variation = std::max(max_variation, max_variations[
i]);
860 const IntegerValue slack = info.rev_rhs - implied_lb;
865 if (slack < 0 || max_variation <= slack)
return {slack, 0};
869 const auto coeffs = GetCoeffs(info);
870 for (
int i = 0;
i < info.rev_size; ++
i) {
871 if (max_variations[
i] <= slack)
continue;
872 std::swap(vars[
i], vars[num_to_push]);
873 std::swap(coeffs[
i], coeffs[num_to_push]);
876 return {slack, num_to_push};
881bool LinearPropagator::PropagateInfeasibleConstraint(
int id,
882 IntegerValue slack) {
884 const ConstraintInfo& info = infos_[id];
885 const auto vars = GetVariables(info);
886 const auto coeffs = GetCoeffs(info);
889 integer_reason_.clear();
890 reason_coeffs_.clear();
891 for (
int i = 0;
i < info.initial_size; ++
i) {
892 const IntegerVariable var = vars[
i];
893 if (!integer_trail_->VariableLowerBoundIsFromLevelZero(var)) {
894 integer_reason_.push_back(integer_trail_->LowerBoundAsLiteral(var));
895 reason_coeffs_.push_back(coeffs[
i]);
900 integer_trail_->RelaxLinearReason(-slack - 1, reason_coeffs_,
902 ++num_enforcement_pushes_;
903 return enforcement_propagator_->PropagateWhenFalse(info.enf_id, {},
908 IntegerVariable var_to_explain,
int trail_index,
909 std::vector<Literal>* literals_reason,
910 std::vector<int>* trail_indices_reason) {
911 literals_reason->clear();
912 trail_indices_reason->clear();
913 const ConstraintInfo& info = infos_[id];
914 enforcement_propagator_->AddEnforcementReason(info.enf_id, literals_reason);
915 reason_coeffs_.clear();
917 const auto coeffs = GetCoeffs(info);
918 const auto vars = GetVariables(info);
919 for (
int i = 0;
i < info.initial_size; ++
i) {
920 const IntegerVariable var = vars[
i];
925 integer_trail_->FindTrailIndexOfVarBefore(var, trail_index);
927 trail_indices_reason->push_back(index);
928 if (propagation_slack > 0) {
929 reason_coeffs_.push_back(coeffs[
i]);
933 if (propagation_slack > 0) {
934 integer_trail_->RelaxLinearReason(propagation_slack, reason_coeffs_,
935 trail_indices_reason);
939bool LinearPropagator::PropagateOneConstraint(
int id) {
940 const auto [slack, num_to_push] = AnalyzeConstraint(
id);
941 if (slack < 0)
return PropagateInfeasibleConstraint(
id, slack);
942 if (num_to_push == 0)
return true;
944 DCHECK_GT(num_to_push, 0);
946 const ConstraintInfo& info = infos_[id];
947 const auto vars = GetVariables(info);
948 const auto coeffs = GetCoeffs(info);
958 if (!DisassembleSubtree(
id, num_to_push)) {
965 for (
int i = 0;
i < num_to_push; ++
i) {
974 const IntegerVariable var = vars[
i];
975 const IntegerValue coeff = coeffs[
i];
976 const IntegerValue div = slack / coeff;
977 const IntegerValue new_ub = integer_trail_->
LowerBound(var) + div;
978 const IntegerValue propagation_slack = (div + 1) * coeff - slack - 1;
986 const IntegerValue actual_ub = integer_trail_->UpperBound(var);
987 const IntegerVariable next_var =
NegationOf(var);
988 if (actual_ub < new_ub) {
990 OnVariableChange(next_var, -actual_ub, -1);
991 }
else if (actual_ub == new_ub) {
992 OnVariableChange(next_var, -actual_ub,
id);
995 std::swap(vars[
i], vars[num_pushed]);
996 std::swap(coeffs[
i], coeffs[num_pushed]);
1007std::string LinearPropagator::ConstraintDebugString(
int id) {
1009 const ConstraintInfo& info = infos_[id];
1010 const auto coeffs = GetCoeffs(info);
1011 const auto vars = GetVariables(info);
1012 IntegerValue implied_lb(0);
1013 IntegerValue rhs_correction(0);
1014 for (
int i = 0;
i < info.initial_size; ++
i) {
1015 const IntegerValue term = coeffs[
i] * integer_trail_->LowerBound(vars[
i]);
1016 if (
i >= info.rev_size) {
1017 rhs_correction += term;
1020 absl::StrAppend(&result,
" +", coeffs[
i].value(),
"*X", vars[
i].value());
1022 const IntegerValue original_rhs = info.rev_rhs + rhs_correction;
1023 absl::StrAppend(&result,
" <= ", original_rhs.value(),
1024 " slack=", original_rhs.value() - implied_lb.value());
1025 absl::StrAppend(&result,
" enf=", info.enf_status);
1029bool LinearPropagator::ReportConflictingCycle() {
1037 literal_reason_.clear();
1038 integer_reason_.clear();
1039 absl::int128 rhs_sum = 0;
1040 absl::flat_hash_map<IntegerVariable, absl::int128> map_sum;
1041 for (
const auto [
id, next_var, increase] : disassemble_branch_) {
1042 const ConstraintInfo& info = infos_[id];
1043 enforcement_propagator_->AddEnforcementReason(info.enf_id,
1045 const auto coeffs = GetCoeffs(info);
1046 const auto vars = GetVariables(info);
1047 IntegerValue rhs_correction(0);
1048 for (
int i = 0;
i < info.initial_size; ++
i) {
1049 if (
i >= info.rev_size) {
1050 rhs_correction += coeffs[
i] * integer_trail_->LowerBound(vars[
i]);
1053 map_sum[vars[
i]] += coeffs[
i].value();
1058 rhs_sum += (info.rev_rhs + rhs_correction).value();
1063 absl::int128 implied_lb = 0;
1064 for (
const auto [var, coeff] : map_sum) {
1066 if (!integer_trail_->VariableLowerBoundIsFromLevelZero(var)) {
1067 integer_reason_.push_back(integer_trail_->LowerBoundAsLiteral(var));
1070 coeff * absl::int128{integer_trail_->LowerBound(var).value()};
1071 }
else if (coeff < 0) {
1072 if (!integer_trail_->VariableLowerBoundIsFromLevelZero(
1074 integer_reason_.push_back(integer_trail_->UpperBoundAsLiteral(var));
1077 coeff * absl::int128{integer_trail_->UpperBound(var).value()};
1080 if (implied_lb > rhs_sum) {
1082 std::sort(integer_reason_.begin(), integer_reason_.end(),
1083 [](
const IntegerLiteral& a,
const IntegerLiteral&
b) {
1084 return a.var < b.var;
1088 const absl::int128 limit{std::numeric_limits<int64_t>::max()};
1089 const absl::int128 slack = implied_lb - rhs_sum;
1091 reason_coeffs_.clear();
1093 for (
const IntegerLiteral i_lit : integer_reason_) {
1100 reason_coeffs_.push_back(
static_cast<int64_t
>(c));
1103 const IntegerValue slack64(
1104 static_cast<int64_t
>(std::min(limit, slack)));
1105 integer_trail_->RelaxLinearReason(slack64 - 1, reason_coeffs_,
1110 ++num_short_reasons_;
1111 VLOG(2) <<
"Simplified " << integer_reason_.size() <<
" slack "
1112 << implied_lb - rhs_sum;
1113 return integer_trail_->ReportConflict(literal_reason_, integer_reason_);
1122 literal_reason_.clear();
1123 integer_reason_.clear();
1125 for (
const auto [
id, next_var, increase] : disassemble_branch_) {
1126 const ConstraintInfo& info = infos_[id];
1127 enforcement_propagator_->AddEnforcementReason(info.enf_id,
1129 for (
const IntegerVariable var : GetVariables(infos_[
id])) {
1132 if (var == previous_var)
continue;
1137 if (!integer_trail_->VariableLowerBoundIsFromLevelZero(var)) {
1138 integer_reason_.push_back(integer_trail_->LowerBoundAsLiteral(var));
1141 previous_var = next_var;
1143 VLOG(2) << next_var <<
" [" << integer_trail_->LowerBound(next_var) <<
","
1144 << integer_trail_->UpperBound(next_var)
1145 <<
"] : " << ConstraintDebugString(
id);
1147 ++num_long_reasons_;
1148 return integer_trail_->ReportConflict(literal_reason_, integer_reason_);
1151std::pair<IntegerValue, IntegerValue> LinearPropagator::GetCycleCoefficients(
1152 int id, IntegerVariable var, IntegerVariable next_var) {
1153 const ConstraintInfo& info = infos_[id];
1154 const auto coeffs = GetCoeffs(info);
1155 const auto vars = GetVariables(info);
1156 IntegerValue var_coeff(0);
1157 IntegerValue next_coeff(0);
1158 for (
int i = 0;
i < info.initial_size; ++
i) {
1159 if (vars[
i] == var) var_coeff = coeffs[
i];
1160 if (vars[
i] ==
NegationOf(next_var)) next_coeff = coeffs[
i];
1162 DCHECK_NE(var_coeff, 0);
1163 DCHECK_NE(next_coeff, 0);
1164 return {var_coeff, next_coeff};
1176bool LinearPropagator::DisassembleSubtree(
int root_id,
int num_tight) {
1182 disassemble_queue_.clear();
1183 disassemble_branch_.clear();
1185 const ConstraintInfo& info = infos_[root_id];
1186 const auto vars = GetVariables(info);
1187 for (
int i = 0;
i < num_tight; ++
i) {
1188 disassemble_queue_.push_back({root_id,
NegationOf(vars[
i]), 1});
1194 absl::Span<int> id_to_count = absl::MakeSpan(id_to_propagation_count_);
1195 while (!disassemble_queue_.empty()) {
1196 const auto [prev_id, var, increase] = disassemble_queue_.back();
1197 if (!disassemble_branch_.empty() &&
1198 disassemble_branch_.back().id == prev_id &&
1199 disassemble_branch_.back().var == var) {
1200 disassemble_branch_.pop_back();
1201 disassemble_queue_.pop_back();
1205 disassemble_branch_.push_back({prev_id, var, increase});
1207 time_limit_->AdvanceDeterministicTime(
1208 static_cast<double>(var_to_constraint_ids_[var].size()) * 1e-9);
1209 for (
const int id : var_to_constraint_ids_[var]) {
1210 if (
id == root_id) {
1214 CHECK(!disassemble_branch_.empty());
1217 const auto [test_id, root_var, var_increase] = disassemble_branch_[0];
1218 CHECK_EQ(test_id, root_id);
1219 CHECK_NE(var, root_var);
1224 const auto [var_coeff, root_coeff] =
1225 GetCycleCoefficients(
id, var, root_var);
1226 if (
CapProdI(var_increase, var_coeff) >= root_coeff) {
1228 return ReportConflictingCycle();
1234 ++num_failed_cycles_;
1238 if (id_to_count[
id] == 0)
continue;
1243 const ConstraintInfo& info = infos_[id];
1244 const auto coeffs = GetCoeffs(info);
1245 const auto vars = GetVariables(info);
1246 IntegerValue var_coeff(0);
1247 disassemble_candidates_.clear();
1248 ++num_explored_in_disassemble_;
1249 time_limit_->AdvanceDeterministicTime(
static_cast<double>(info.rev_size) *
1251 for (
int i = 0;
i < info.rev_size; ++
i) {
1252 if (vars[
i] == var) {
1253 var_coeff = coeffs[
i];
1256 const IntegerVariable next_var =
NegationOf(vars[
i]);
1257 if (propagated_by_[next_var] ==
id) {
1258 disassemble_candidates_.push_back({next_var, coeffs[
i]});
1261 propagated_by_[next_var] = -1;
1266 for (
const auto [next_var, next_var_coeff] : disassemble_candidates_) {
1272 const IntegerValue next_increase =
1274 if (next_increase > 0) {
1275 disassemble_queue_.push_back({id, next_var, next_increase});
1290void LinearPropagator::AddToQueueIfNeeded(
int id) {
1291 DCHECK_LT(
id, in_queue_.size());
1292 DCHECK_LT(
id, infos_.size());
1294 if (in_queue_[
id])
return;
1296 propagation_queue_.push_back(
id);
1299void LinearPropagator::ClearPropagatedBy() {
1302 for (
const IntegerVariable var :
1303 propagated_by_was_set_.PositionsSetAtLeastOnce()) {
1304 int&
id = propagated_by_[var];
1305 if (
id != -1) --id_to_propagation_count_[id];
1306 propagated_by_[var] = -1;
1308 propagated_by_was_set_.ClearAndResize(propagated_by_was_set_.size());
1309 DCHECK(std::all_of(propagated_by_.begin(), propagated_by_.end(),
1310 [](
int id) { return id == -1; }));
1311 DCHECK(std::all_of(id_to_propagation_count_.begin(),
1312 id_to_propagation_count_.end(),
1313 [](
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)
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)
EnforcementStatus DebugStatus(EnforcementId id)
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
ABSL_MUST_USE_RESULT bool EnqueueWithLazyReason(IntegerLiteral i_lit, int id, IntegerValue propagation_slack, LazyReasonInterface *explainer)
Lazy reason API.
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.
LiteralIndex NegatedIndex() const
LiteralIndex Index() const
SatPropagator(const std::string &name)
int propagation_trail_index_
void AddPropagator(SatPropagator *propagator)
Simple class to add statistics by name and print them at the end.
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
const LiteralIndex kNoLiteralIndex(-1)
std::vector< IntegerVariable > NegationOf(absl::Span< const 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.
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)