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/log/log.h"
33#include "absl/log/vlog_is_on.h"
34#include "absl/numeric/int128.h"
35#include "absl/strings/str_cat.h"
36#include "absl/types/span.h"
60 os <<
"CANNOT_PROPAGATE";
63 os <<
"CAN_PROPAGATE";
74 trail_(*model->GetOrCreate<
Trail>()),
87 rev_int_repository_->SaveStateWithStamp(&rev_stack_size_, &rev_stamp_);
90 if (literal.
Index() >=
static_cast<int>(watcher_.size()))
continue;
93 auto& watch_list = watcher_[literal.
Index()];
94 for (
const EnforcementId
id : watch_list) {
95 const LiteralIndex index = ProcessIdOnTrue(literal,
id);
98 watch_list[new_size++] = id;
101 CHECK_NE(index, literal.
Index());
102 watcher_[index].push_back(
id);
105 watch_list.resize(new_size);
108 for (
const EnforcementId
id : watcher_[literal.
NegatedIndex()]) {
112 rev_stack_size_ =
static_cast<int>(untrail_stack_.size());
116 for (
const EnforcementId
id : ids_to_fix_until_next_root_level_) {
119 if (trail_.CurrentDecisionLevel() == 0) {
120 ids_to_fix_until_next_root_level_.clear();
128 const int size =
static_cast<int>(untrail_stack_.size());
129 for (
int i = size - 1;
i >= rev_stack_size_; --
i) {
130 const auto [id, status] = untrail_stack_[
i];
131 statuses_[id] = status;
132 if (callbacks_[
id] !=
nullptr) callbacks_[id](id, status);
134 untrail_stack_.resize(rev_stack_size_);
144 absl::Span<const Literal> enforcement,
148 bool is_always_false =
false;
149 temp_literals_.clear();
150 const int level = trail_.CurrentDecisionLevel();
151 for (
const Literal l : enforcement) {
153 const int size = std::max(l.Index().value(), l.NegatedIndex().value()) + 1;
154 if (size >
static_cast<int>(watcher_.size())) {
155 watcher_.resize(size);
157 if (assignment_.LiteralIsTrue(l)) {
158 if (level == 0 || trail_.Info(l.Variable()).level == 0)
continue;
160 }
else if (assignment_.LiteralIsFalse(l)) {
161 if (level == 0 || trail_.Info(l.Variable()).level == 0) {
162 is_always_false =
true;
167 temp_literals_.push_back(l);
172 if (is_always_false) {
173 if (callback !=
nullptr)
175 return EnforcementId(-1);
177 if (temp_literals_.empty()) {
178 if (callback !=
nullptr)
180 return EnforcementId(-1);
183 const EnforcementId id(
static_cast<int>(callbacks_.size()));
184 callbacks_.push_back(std::move(callback));
186 CHECK(!temp_literals_.empty());
187 buffer_.insert(buffer_.end(), temp_literals_.begin(), temp_literals_.end());
188 starts_.push_back(buffer_.size());
191 statuses_.push_back(temp_literals_.size() == 1
195 if (temp_literals_.size() == 1) {
196 watcher_[temp_literals_[0].Index()].push_back(
id);
199 const auto span = GetSpan(
id);
200 int num_not_true = 0;
201 for (
int i = 0;
i < span.size(); ++
i) {
202 if (assignment_.LiteralIsTrue(span[
i]))
continue;
203 std::swap(span[num_not_true], span[
i]);
205 if (num_not_true == 2)
break;
209 if (num_not_true == 1) {
210 int max_level = trail_.Info(span[1].
Variable()).level;
211 for (
int i = 2;
i < span.size(); ++
i) {
212 const int level = trail_.Info(span[
i].
Variable()).level;
213 if (level > max_level) {
215 std::swap(span[1], span[
i]);
220 watcher_[span[0].Index()].push_back(
id);
221 watcher_[span[1].Index()].push_back(
id);
228 }
else if (num_true == temp_literals_.size()) {
230 }
else if (num_true + 1 == temp_literals_.size()) {
233 if (temp_literals_.size() == 1) {
234 if (callbacks_[
id] !=
nullptr) {
242 if (trail_.CurrentDecisionLevel() > 0 &&
244 ids_to_fix_until_next_root_level_.push_back(
id);
252 EnforcementId
id, std::vector<Literal>* reason)
const {
253 for (
const Literal l : GetSpan(
id)) {
254 reason->push_back(l.Negated());
261 EnforcementId
id, absl::Span<const Literal> literal_reason,
262 absl::Span<const IntegerLiteral> integer_reason) {
263 temp_reason_.clear();
265 for (
const Literal l : GetSpan(
id)) {
266 if (assignment_.LiteralIsFalse(l))
return true;
267 if (assignment_.LiteralIsTrue(l)) {
268 temp_reason_.push_back(l.Negated());
272 unique_unassigned = l.Index();
275 temp_reason_.insert(temp_reason_.end(), literal_reason.begin(),
276 literal_reason.end());
278 return integer_trail_->ReportConflict(temp_reason_, integer_reason);
283 integer_trail_->EnqueueLiteral(
Literal(unique_unassigned).Negated(),
284 temp_reason_, integer_reason);
288absl::Span<Literal> EnforcementPropagator::GetSpan(EnforcementId
id) {
289 if (
id < 0)
return {};
290 DCHECK_LE(
id + 1, starts_.size());
291 const int size = starts_[
id + 1] - starts_[id];
293 return absl::MakeSpan(&buffer_[starts_[
id]], size);
296absl::Span<const Literal> EnforcementPropagator::GetSpan(
297 EnforcementId
id)
const {
298 if (
id < 0)
return {};
299 DCHECK_LE(
id + 1, starts_.size());
300 const int size = starts_[
id + 1] - starts_[id];
302 return absl::MakeSpan(&buffer_[starts_[
id]], size);
305LiteralIndex EnforcementPropagator::ProcessIdOnTrue(
Literal watched,
310 const auto span = GetSpan(
id);
311 if (span.size() == 1) {
317 const int watched_pos = (span[0] == watched) ? 0 : 1;
318 CHECK_EQ(span[watched_pos], watched);
319 if (assignment_.LiteralIsFalse(span[watched_pos ^ 1])) {
324 for (
int i = 2;
i < span.size(); ++
i) {
325 const Literal l = span[
i];
326 if (assignment_.LiteralIsFalse(l)) {
330 if (!assignment_.LiteralIsAssigned(l)) {
333 std::swap(span[watched_pos], span[
i]);
334 return span[watched_pos].Index();
339 if (assignment_.LiteralIsTrue(span[watched_pos ^ 1])) {
351void EnforcementPropagator::ChangeStatus(EnforcementId
id,
354 if (old_status == new_status)
return;
355 if (trail_.CurrentDecisionLevel() != 0) {
356 untrail_stack_.push_back({id, old_status});
358 statuses_[id] = new_status;
359 if (callbacks_[
id] !=
nullptr) callbacks_[id](id, new_status);
366 for (
const Literal l : GetSpan(
id)) {
367 if (assignment_.LiteralIsFalse(l)) {
370 if (assignment_.LiteralIsTrue(l)) ++num_true;
372 const int size = GetSpan(
id).size();
379 : trail_(model->GetOrCreate<
Trail>()),
383 time_limit_(model->GetOrCreate<
TimeLimit>()),
385 rev_integer_value_repository_(
391 watcher_id_(watcher_->Register(this)),
392 order_(random_, time_limit_,
393 [this](int id) {
return GetVariables(infos_[
id]); }) {
395 integer_trail_->RegisterWatcher(&modified_vars_);
396 integer_trail_->RegisterReversibleClass(
this);
401 watcher_->SetPropagatorPriority(watcher_id_, 0);
405 if (!VLOG_IS_ON(1))
return;
406 if (shared_stats_ ==
nullptr)
return;
407 std::vector<std::pair<std::string, int64_t>> stats;
408 stats.push_back({
"linear_propag/num_pushes", num_pushes_});
410 {
"linear_propag/num_enforcement_pushes", num_enforcement_pushes_});
412 stats.push_back({
"linear_propag/num_cycles", num_cycles_});
413 stats.push_back({
"linear_propag/num_failed_cycles", num_failed_cycles_});
415 stats.push_back({
"linear_propag/num_short_reasons_", num_short_reasons_});
416 stats.push_back({
"linear_propag/num_long_reasons_", num_long_reasons_});
418 stats.push_back({
"linear_propag/num_scanned", num_scanned_});
419 stats.push_back({
"linear_propag/num_explored_in_disassemble",
420 num_explored_in_disassemble_});
421 stats.push_back({
"linear_propag/num_bool_aborts", num_bool_aborts_});
422 stats.push_back({
"linear_propag/num_loop_aborts", num_loop_aborts_});
423 stats.push_back({
"linear_propag/num_ignored", num_ignored_});
424 stats.push_back({
"linear_propag/num_delayed", num_delayed_});
425 shared_stats_->AddStats(stats);
429 if (level < previous_level_) {
431 modified_vars_.ResetAllToFalse();
436 for (
const int id : propagation_queue_) in_queue_.Clear(
id);
437 propagation_queue_.clear();
438 for (
int i = rev_unenforced_size_;
i < unenforced_constraints_.size();
440 in_queue_.Clear(unenforced_constraints_[
i]);
442 unenforced_constraints_.resize(rev_unenforced_size_);
443 }
else if (level > previous_level_) {
444 rev_unenforced_size_ = unenforced_constraints_.size();
445 rev_int_repository_->SaveState(&rev_unenforced_size_);
452 if (!propagation_queue_.empty() ||
453 !modified_vars_.PositionsSetAtLeastOnce().empty() || !order_.IsEmpty()) {
454 watcher_->CallOnNextPropagate(watcher_id_);
457 previous_level_ = level;
460void LinearPropagator::SetPropagatedBy(IntegerVariable var,
int id) {
461 int& ref_id = propagated_by_[var];
462 if (ref_id ==
id)
return;
464 propagated_by_was_set_.
Set(var);
467 DCHECK_LT(var, propagated_by_.size());
469 DCHECK_GE(ref_id, 0);
470 DCHECK_LT(ref_id, id_to_propagation_count_.size());
471 id_to_propagation_count_[ref_id]--;
474 if (
id != -1) id_to_propagation_count_[id]++;
477void LinearPropagator::OnVariableChange(IntegerVariable var, IntegerValue lb,
479 DCHECK_EQ(lb, integer_trail_->
LowerBound(var));
482 const int size = var_to_constraint_ids_[var].size();
483 if (size == 0)
return;
485 SetPropagatedBy(var,
id);
487 Bitset64<int>::View in_queue = in_queue_.
view();
489 for (
const int id : var_to_constraint_ids_[var]) {
490 if (in_queue[
id])
continue;
492 propagation_queue_.push_back(
id);
501 for (
const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
502 if (var >= var_to_constraint_ids_.size())
continue;
503 OnVariableChange(var, integer_trail_->LowerBound(var), -1);
507 num_terms_for_dtime_update_ = 0;
508 const auto cleanup = ::absl::MakeCleanup([
this]() {
509 time_limit_->AdvanceDeterministicTime(
510 static_cast<double>(num_terms_for_dtime_update_) * 1e-9);
511 modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
541 const bool push_affine_ub = push_affine_ub_for_binary_relations_;
546 const int saved_index = trail_->Index();
547 while (!propagation_queue_.empty()) {
548 const int id = propagation_queue_.front();
549 propagation_queue_.pop_front();
551 const auto [slack, num_to_push] = AnalyzeConstraint(
id);
555 if (!PropagateInfeasibleConstraint(
id, slack))
return false;
562 if (trail_->Index() > saved_index) {
566 }
else if (num_to_push > 0) {
569 const auto vars = GetVariables(infos_[
id]);
570 const auto coeffs = GetCoeffs(infos_[
id]);
571 for (
int i = 0;
i < num_to_push; ++
i) {
572 const IntegerVariable var = vars[
i];
573 const IntegerValue coeff = coeffs[
i];
574 const IntegerValue div = slack / coeff;
575 const IntegerValue new_ub = integer_trail_->LowerBound(var) + div;
576 order_.Register(
id,
NegationOf(var), -new_ub);
595 const ConstraintInfo& info = infos_[id];
596 if (push_affine_ub && info.initial_size == 3 && info.enf_id == -1) {
598 const auto vars = GetVariables(info);
599 const auto coeffs = GetCoeffs(info);
604 if (info.rev_size == 2) {
606 expr.
vars[0] = vars[0];
607 expr.
vars[1] = vars[1];
608 expr.
coeffs[0] = coeffs[0];
609 expr.
coeffs[1] = coeffs[1];
613 const IntegerValue initial_rhs =
614 info.rev_rhs + coeffs[2] * integer_trail_->LowerBound(vars[2]);
615 binary_relations_->AddAffineUpperBound(
617 }
else if (info.rev_size == 3) {
618 for (
int i = 0;
i < 3; ++
i) {
620 const int a = (
i + 1) % 3;
621 const int b = (
i + 2) % 3;
622 expr.
vars[0] = vars[a];
623 expr.
vars[1] = vars[
b];
624 expr.
coeffs[0] = coeffs[a];
626 binary_relations_->AddAffineUpperBound(
633 const int next_id = order_.NextId();
634 if (next_id == -1)
break;
639 if (!PropagateOneConstraint(next_id))
return false;
644 if (trail_->Index() > saved_index) {
654 absl::Span<const Literal> enforcement_literals,
655 absl::Span<const IntegerVariable> vars,
656 absl::Span<const IntegerValue> coeffs, IntegerValue upper_bound) {
657 if (vars.empty())
return true;
658 if (trail_->CurrentDecisionLevel() == 0) {
659 for (
const Literal l : enforcement_literals) {
660 if (trail_->Assignment().LiteralIsFalse(l))
return true;
666 CHECK_LT(vars.size(), 1 << 29);
667 if (vars.size() > max_variations_.size()) {
668 max_variations_.resize(vars.size(), 0);
669 buffer_of_ones_.resize(vars.size(), IntegerValue(1));
673 CHECK_EQ(vars.size(), coeffs.size());
674 const int id = infos_.size();
677 info.all_coeffs_are_one =
false;
678 info.start = variables_buffer_.size();
679 info.initial_size = vars.size();
680 info.rev_rhs = upper_bound;
681 info.rev_size = vars.size();
682 infos_.push_back(std::move(info));
683 initial_rhs_.push_back(upper_bound);
686 id_to_propagation_count_.push_back(0);
687 variables_buffer_.insert(variables_buffer_.end(), vars.begin(), vars.end());
688 coeffs_buffer_.insert(coeffs_buffer_.end(), coeffs.begin(), coeffs.end());
689 CanonicalizeConstraint(
id);
691 bool all_at_one =
true;
692 for (
const IntegerValue coeff : GetCoeffs(infos_.back())) {
701 infos_.back().all_coeffs_are_one =
true;
706 in_queue_.resize(in_queue_.size() + 1);
708 if (!enforcement_literals.empty()) {
709 infos_.back().enf_status =
711 infos_.back().enf_id = enforcement_propagator_->Register(
712 enforcement_literals,
714 infos_[id].enf_status =
static_cast<int>(status);
720 AddToQueueIfNeeded(
id);
721 watcher_->CallOnNextPropagate(watcher_id_);
729 const auto info = infos_[id];
730 if (info.initial_size == 2) {
731 const auto vars = GetVariables(info);
732 const auto coeffs = GetCoeffs(info);
733 precedences_->PushConditionalRelation(
734 enforcement_propagator_->GetEnforcementLiterals(enf_id),
743 AddToQueueIfNeeded(
id);
744 infos_.back().enf_id = -1;
748 order_.Resize(var_to_constraint_ids_.size(), in_queue_.size());
749 for (
const IntegerVariable var : GetVariables(infos_[
id])) {
751 if (var >= var_to_constraint_ids_.size()) {
753 const int size = std::max(var,
NegationOf(var)).value() + 1;
754 var_to_constraint_ids_.resize(size);
755 propagated_by_.resize(size, -1);
756 propagated_by_was_set_.Resize(IntegerVariable(size));
757 is_watched_.resize(size,
false);
759 order_.Resize(size, in_queue_.size());
765 var_to_constraint_ids_[var].push_back(
id);
769 if (!is_watched_[var]) {
770 is_watched_[var] =
true;
771 watcher_->WatchLowerBound(var, watcher_id_);
777 num_terms_for_dtime_update_ = 0;
778 const auto cleanup = ::absl::MakeCleanup([
this]() {
779 time_limit_->AdvanceDeterministicTime(
780 static_cast<double>(num_terms_for_dtime_update_) * 1e-9);
782 if (!PropagateOneConstraint(
id))
return false;
786absl::Span<IntegerValue> LinearPropagator::GetCoeffs(
787 const ConstraintInfo& info) {
788 if (info.all_coeffs_are_one) {
789 return absl::MakeSpan(&buffer_of_ones_[0], info.initial_size);
791 return absl::MakeSpan(&coeffs_buffer_[info.start], info.initial_size);
794absl::Span<IntegerVariable> LinearPropagator::GetVariables(
795 const ConstraintInfo& info) {
796 return absl::MakeSpan(&variables_buffer_[info.start], info.initial_size);
799void LinearPropagator::CanonicalizeConstraint(
int id) {
800 const ConstraintInfo& info = infos_[id];
801 const auto coeffs = GetCoeffs(info);
802 const auto vars = GetVariables(info);
803 for (
int i = 0;
i < vars.size(); ++
i) {
805 coeffs[
i] = -coeffs[
i];
813 absl::flat_hash_set<IntegerVariable> no_dup;
814 for (
const IntegerVariable var : GetVariables(info)) {
822std::pair<IntegerValue, int> LinearPropagator::AnalyzeConstraint(
int id) {
826 ConstraintInfo& info = infos_[id];
828 if (
DEBUG_MODE && enforcement_propagator_->PropagationIsDone(*trail_)) {
830 enforcement_propagator_->DebugStatus(info.enf_id);
831 if (enf_status != debug_status) {
838 LOG(FATAL) <<
"Enforcement status not up to date: " << enf_status
839 <<
" vs debug: " << debug_status;
846 DCHECK(!in_queue_[
id]);
851 unenforced_constraints_.push_back(
id);
859 IntegerValue implied_lb(0);
860 const auto vars = GetVariables(info);
861 IntegerValue max_variation(0);
862 bool first_change =
true;
863 num_terms_for_dtime_update_ += info.rev_size;
864 IntegerValue* max_variations = max_variations_.data();
865 const IntegerValue* lower_bounds = integer_trail_->LowerBoundsData();
866 if (info.all_coeffs_are_one) {
868 for (
int i = 0;
i < info.rev_size;) {
869 const IntegerVariable var = vars[
i];
870 const IntegerValue lb = lower_bounds[var.value()];
871 const IntegerValue diff = -lower_bounds[
NegationOf(var).value()] - lb;
876 rev_int_repository_->SaveState(&info.rev_size);
877 rev_integer_value_repository_->SaveState(&info.rev_rhs);
878 first_change =
false;
881 std::swap(vars[
i], vars[info.rev_size]);
885 max_variations[
i] = diff;
886 max_variation = std::max(max_variation, diff);
891 const auto coeffs = GetCoeffs(info);
892 for (
int i = 0;
i < info.rev_size;) {
893 const IntegerVariable var = vars[
i];
894 const IntegerValue coeff = coeffs[
i];
895 const IntegerValue lb = lower_bounds[var.value()];
896 const IntegerValue diff = -lower_bounds[
NegationOf(var).value()] - lb;
901 rev_int_repository_->SaveState(&info.rev_size);
902 rev_integer_value_repository_->SaveState(&info.rev_rhs);
903 first_change =
false;
906 std::swap(vars[
i], vars[info.rev_size]);
907 std::swap(coeffs[
i], coeffs[info.rev_size]);
908 info.rev_rhs -= coeff * lb;
910 implied_lb += coeff * lb;
911 max_variations[
i] = diff * coeff;
912 max_variation = std::max(max_variation, max_variations[
i]);
920 const IntegerValue slack = info.rev_rhs - implied_lb;
925 if (slack < 0 || max_variation <= slack)
return {slack, 0};
929 const auto coeffs = GetCoeffs(info);
930 for (
int i = 0;
i < info.rev_size; ++
i) {
931 if (max_variations[
i] <= slack)
continue;
932 std::swap(vars[
i], vars[num_to_push]);
933 std::swap(coeffs[
i], coeffs[num_to_push]);
936 return {slack, num_to_push};
941bool LinearPropagator::PropagateInfeasibleConstraint(
int id,
942 IntegerValue slack) {
944 const ConstraintInfo& info = infos_[id];
945 const auto vars = GetVariables(info);
946 const auto coeffs = GetCoeffs(info);
949 integer_reason_.clear();
950 reason_coeffs_.clear();
951 for (
int i = 0;
i < info.initial_size; ++
i) {
952 const IntegerVariable var = vars[
i];
953 if (!integer_trail_->VariableLowerBoundIsFromLevelZero(var)) {
954 integer_reason_.push_back(integer_trail_->LowerBoundAsLiteral(var));
955 reason_coeffs_.push_back(coeffs[
i]);
960 integer_trail_->RelaxLinearReason(-slack - 1, reason_coeffs_,
962 ++num_enforcement_pushes_;
963 return enforcement_propagator_->PropagateWhenFalse(info.enf_id, {},
968 IntegerVariable var_to_explain,
int trail_index,
969 std::vector<Literal>* literals_reason,
970 std::vector<int>* trail_indices_reason) {
971 literals_reason->clear();
972 trail_indices_reason->clear();
973 const ConstraintInfo& info = infos_[id];
974 enforcement_propagator_->AddEnforcementReason(info.enf_id, literals_reason);
975 reason_coeffs_.clear();
977 const auto coeffs = GetCoeffs(info);
978 const auto vars = GetVariables(info);
979 for (
int i = 0;
i < info.initial_size; ++
i) {
980 const IntegerVariable var = vars[
i];
985 integer_trail_->FindTrailIndexOfVarBefore(var, trail_index);
987 trail_indices_reason->push_back(index);
988 if (propagation_slack > 0) {
989 reason_coeffs_.push_back(coeffs[
i]);
993 if (propagation_slack > 0) {
994 integer_trail_->RelaxLinearReason(propagation_slack, reason_coeffs_,
995 trail_indices_reason);
999bool LinearPropagator::PropagateOneConstraint(
int id) {
1000 const auto [slack, num_to_push] = AnalyzeConstraint(
id);
1001 if (slack < 0)
return PropagateInfeasibleConstraint(
id, slack);
1002 if (num_to_push == 0)
return true;
1004 DCHECK_GT(num_to_push, 0);
1005 DCHECK_GE(slack, 0);
1006 const ConstraintInfo& info = infos_[id];
1007 const auto vars = GetVariables(info);
1008 const auto coeffs = GetCoeffs(info);
1018 if (!DisassembleSubtree(
id, num_to_push)) {
1025 for (
int i = 0;
i < num_to_push; ++
i) {
1034 const IntegerVariable var = vars[
i];
1035 const IntegerValue coeff = coeffs[
i];
1036 const IntegerValue div = slack / coeff;
1037 const IntegerValue new_ub = integer_trail_->
LowerBound(var) + div;
1038 const IntegerValue propagation_slack = (div + 1) * coeff - slack - 1;
1046 const IntegerValue actual_ub = integer_trail_->UpperBound(var);
1047 const IntegerVariable next_var =
NegationOf(var);
1048 if (actual_ub < new_ub) {
1050 OnVariableChange(next_var, -actual_ub, -1);
1051 }
else if (actual_ub == new_ub) {
1052 OnVariableChange(next_var, -actual_ub,
id);
1055 std::swap(vars[
i], vars[num_pushed]);
1056 std::swap(coeffs[
i], coeffs[num_pushed]);
1067std::string LinearPropagator::ConstraintDebugString(
int id) {
1069 const ConstraintInfo& info = infos_[id];
1070 const auto coeffs = GetCoeffs(info);
1071 const auto vars = GetVariables(info);
1072 IntegerValue implied_lb(0);
1073 IntegerValue rhs_correction(0);
1074 for (
int i = 0;
i < info.initial_size; ++
i) {
1075 const IntegerValue term = coeffs[
i] * integer_trail_->LowerBound(vars[
i]);
1076 if (
i >= info.rev_size) {
1077 rhs_correction += term;
1080 absl::StrAppend(&result,
" +", coeffs[
i].value(),
"*X", vars[
i].value());
1082 const IntegerValue original_rhs = info.rev_rhs + rhs_correction;
1083 absl::StrAppend(&result,
" <= ", original_rhs.value(),
1084 " slack=", original_rhs.value() - implied_lb.value());
1085 absl::StrAppend(&result,
" enf=", info.enf_status);
1089bool LinearPropagator::ReportConflictingCycle() {
1097 literal_reason_.clear();
1098 integer_reason_.clear();
1099 absl::int128 rhs_sum = 0;
1100 absl::flat_hash_map<IntegerVariable, absl::int128> map_sum;
1101 for (
const auto [
id, next_var, increase] : disassemble_branch_) {
1102 const ConstraintInfo& info = infos_[id];
1103 enforcement_propagator_->AddEnforcementReason(info.enf_id,
1105 const auto coeffs = GetCoeffs(info);
1106 const auto vars = GetVariables(info);
1107 IntegerValue rhs_correction(0);
1108 for (
int i = 0;
i < info.initial_size; ++
i) {
1109 if (
i >= info.rev_size) {
1110 rhs_correction += coeffs[
i] * integer_trail_->LowerBound(vars[
i]);
1113 map_sum[vars[
i]] += coeffs[
i].value();
1118 rhs_sum += (info.rev_rhs + rhs_correction).value();
1123 absl::int128 implied_lb = 0;
1124 for (
const auto [var, coeff] : map_sum) {
1126 if (!integer_trail_->VariableLowerBoundIsFromLevelZero(var)) {
1127 integer_reason_.push_back(integer_trail_->LowerBoundAsLiteral(var));
1130 coeff * absl::int128{integer_trail_->LowerBound(var).value()};
1131 }
else if (coeff < 0) {
1132 if (!integer_trail_->VariableLowerBoundIsFromLevelZero(
1134 integer_reason_.push_back(integer_trail_->UpperBoundAsLiteral(var));
1137 coeff * absl::int128{integer_trail_->UpperBound(var).value()};
1140 if (implied_lb > rhs_sum) {
1142 std::sort(integer_reason_.begin(), integer_reason_.end(),
1143 [](
const IntegerLiteral& a,
const IntegerLiteral&
b) {
1144 return a.var < b.var;
1148 const absl::int128 limit{std::numeric_limits<int64_t>::max()};
1149 const absl::int128 slack = implied_lb - rhs_sum;
1151 reason_coeffs_.clear();
1153 for (
const IntegerLiteral i_lit : integer_reason_) {
1160 reason_coeffs_.push_back(
static_cast<int64_t
>(c));
1163 const IntegerValue slack64(
1164 static_cast<int64_t
>(std::min(limit, slack)));
1165 integer_trail_->RelaxLinearReason(slack64 - 1, reason_coeffs_,
1170 ++num_short_reasons_;
1171 VLOG(2) <<
"Simplified " << integer_reason_.size() <<
" slack "
1172 << implied_lb - rhs_sum;
1173 return integer_trail_->ReportConflict(literal_reason_, integer_reason_);
1182 literal_reason_.clear();
1183 integer_reason_.clear();
1185 for (
const auto [
id, next_var, increase] : disassemble_branch_) {
1186 const ConstraintInfo& info = infos_[id];
1187 enforcement_propagator_->AddEnforcementReason(info.enf_id,
1189 for (
const IntegerVariable var : GetVariables(infos_[
id])) {
1192 if (var == previous_var)
continue;
1197 if (!integer_trail_->VariableLowerBoundIsFromLevelZero(var)) {
1198 integer_reason_.push_back(integer_trail_->LowerBoundAsLiteral(var));
1201 previous_var = next_var;
1203 VLOG(2) << next_var <<
" [" << integer_trail_->LowerBound(next_var) <<
","
1204 << integer_trail_->UpperBound(next_var)
1205 <<
"] : " << ConstraintDebugString(
id);
1207 ++num_long_reasons_;
1208 return integer_trail_->ReportConflict(literal_reason_, integer_reason_);
1211std::pair<IntegerValue, IntegerValue> LinearPropagator::GetCycleCoefficients(
1212 int id, IntegerVariable var, IntegerVariable next_var) {
1213 const ConstraintInfo& info = infos_[id];
1214 const auto coeffs = GetCoeffs(info);
1215 const auto vars = GetVariables(info);
1216 IntegerValue var_coeff(0);
1217 IntegerValue next_coeff(0);
1218 for (
int i = 0;
i < info.initial_size; ++
i) {
1219 if (vars[
i] == var) var_coeff = coeffs[
i];
1220 if (vars[
i] ==
NegationOf(next_var)) next_coeff = coeffs[
i];
1222 DCHECK_NE(var_coeff, 0);
1223 DCHECK_NE(next_coeff, 0);
1224 return {var_coeff, next_coeff};
1236bool LinearPropagator::DisassembleSubtree(
int root_id,
int num_tight) {
1242 disassemble_queue_.clear();
1243 disassemble_branch_.clear();
1245 const ConstraintInfo& info = infos_[root_id];
1246 const auto vars = GetVariables(info);
1247 for (
int i = 0;
i < num_tight; ++
i) {
1248 disassemble_queue_.push_back({root_id,
NegationOf(vars[
i]), 1});
1254 absl::Span<int> id_to_count = absl::MakeSpan(id_to_propagation_count_);
1255 while (!disassemble_queue_.empty()) {
1256 const auto [prev_id, var, increase] = disassemble_queue_.back();
1257 if (!disassemble_branch_.empty() &&
1258 disassemble_branch_.back().id == prev_id &&
1259 disassemble_branch_.back().var == var) {
1260 disassemble_branch_.pop_back();
1261 disassemble_queue_.pop_back();
1265 disassemble_branch_.push_back({prev_id, var, increase});
1267 time_limit_->AdvanceDeterministicTime(
1268 static_cast<double>(var_to_constraint_ids_[var].size()) * 1e-9);
1269 for (
const int id : var_to_constraint_ids_[var]) {
1270 if (
id == root_id) {
1274 CHECK(!disassemble_branch_.empty());
1277 const auto [test_id, root_var, var_increase] = disassemble_branch_[0];
1278 CHECK_EQ(test_id, root_id);
1279 CHECK_NE(var, root_var);
1284 const auto [var_coeff, root_coeff] =
1285 GetCycleCoefficients(
id, var, root_var);
1286 if (
CapProdI(var_increase, var_coeff) >= root_coeff) {
1288 return ReportConflictingCycle();
1294 ++num_failed_cycles_;
1298 if (id_to_count[
id] == 0)
continue;
1303 const ConstraintInfo& info = infos_[id];
1304 const auto coeffs = GetCoeffs(info);
1305 const auto vars = GetVariables(info);
1306 IntegerValue var_coeff(0);
1307 disassemble_candidates_.clear();
1308 ++num_explored_in_disassemble_;
1309 time_limit_->AdvanceDeterministicTime(
static_cast<double>(info.rev_size) *
1311 for (
int i = 0;
i < info.rev_size; ++
i) {
1312 if (vars[
i] == var) {
1313 var_coeff = coeffs[
i];
1316 const IntegerVariable next_var =
NegationOf(vars[
i]);
1317 if (propagated_by_[next_var] ==
id) {
1318 disassemble_candidates_.push_back({next_var, coeffs[
i]});
1321 propagated_by_[next_var] = -1;
1326 for (
const auto [next_var, next_var_coeff] : disassemble_candidates_) {
1332 const IntegerValue next_increase =
1334 if (next_increase > 0) {
1335 disassemble_queue_.push_back({id, next_var, next_increase});
1350void LinearPropagator::AddToQueueIfNeeded(
int id) {
1351 DCHECK_LT(
id, in_queue_.size());
1352 DCHECK_LT(
id, infos_.size());
1354 if (in_queue_[
id])
return;
1356 propagation_queue_.push_back(
id);
1359void LinearPropagator::ClearPropagatedBy() {
1362 for (
const IntegerVariable var :
1363 propagated_by_was_set_.PositionsSetAtLeastOnce()) {
1364 int&
id = propagated_by_[var];
1365 if (
id != -1) --id_to_propagation_count_[id];
1366 propagated_by_[var] = -1;
1368 propagated_by_was_set_.ClearAndResize(propagated_by_was_set_.size());
1369 DCHECK(std::all_of(propagated_by_.begin(), propagated_by_.end(),
1370 [](
int id) { return id == -1; }));
1371 DCHECK(std::all_of(id_to_propagation_count_.begin(),
1372 id_to_propagation_count_.end(),
1373 [](
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)