25#include "absl/algorithm/container.h"
26#include "absl/container/flat_hash_map.h"
27#include "absl/container/flat_hash_set.h"
28#include "absl/log/check.h"
29#include "absl/log/log.h"
30#include "absl/types/span.h"
48 absl::Span<const int64_t>
solution) {
49 int64_t result = expr.offset();
50 for (
int i = 0;
i < expr.vars_size(); ++
i) {
51 result +=
solution[expr.vars(
i)] * expr.coeffs(
i);
57 absl::Span<const int64_t>
solution) {
58 if (affine.coeff == 0)
return affine.offset;
59 return affine.coeff *
solution[affine.var] + affine.offset;
66 result.mutable_vars()->Reserve(a.vars().size() +
b.vars().size());
67 result.mutable_coeffs()->Reserve(a.vars().size() +
b.vars().size());
69 for (
int i = 0;
i < p.vars().size(); ++
i) {
70 result.add_vars(p.vars(
i));
71 result.add_coeffs(p.coeffs(
i));
80 for (int64_t& coeff : *result.mutable_coeffs()) {
87 int64_t result = expr.offset();
88 for (
int i = 0;
i < expr.vars_size(); ++
i) {
90 if (expr.coeffs(
i) > 0) {
91 result += expr.coeffs(
i) * var_proto.domain(0);
93 result += expr.coeffs(
i) * var_proto.domain(var_proto.domain_size() - 1);
100 int64_t result = expr.offset();
101 for (
int i = 0;
i < expr.vars_size(); ++
i) {
103 if (expr.coeffs(
i) > 0) {
104 result += expr.coeffs(
i) * var_proto.domain(var_proto.domain_size() - 1);
106 result += expr.coeffs(
i) * var_proto.domain(0);
112bool LiteralValue(
int lit, absl::Span<const int64_t>
solution) {
125 DCHECK(creation_phase_);
126 domains_.push_back(domain);
127 offsets_.push_back(0);
128 activities_.push_back(0);
129 num_false_enforcement_.push_back(0);
130 distances_.push_back(0);
131 is_violated_.push_back(
false);
132 return num_constraints_++;
136 DCHECK(creation_phase_);
138 if (literal_entries_.size() <= var) {
139 literal_entries_.resize(var + 1);
141 literal_entries_[var].push_back(
147 DCHECK(creation_phase_);
149 AddTerm(ct_index, lit, coeff, 0);
157 DCHECK(creation_phase_);
159 if (coeff == 0)
return;
161 if (var_entries_.size() <= var) {
162 var_entries_.resize(var + 1);
164 if (!var_entries_[var].empty() &&
165 var_entries_[var].back().ct_index == ct_index) {
166 var_entries_[var].back().coefficient += coeff;
167 if (var_entries_[var].back().coefficient == 0) {
168 var_entries_[var].pop_back();
171 var_entries_[var].push_back({.ct_index = ct_index, .coefficient = coeff});
178 DCHECK(creation_phase_);
179 offsets_[ct_index] += offset;
184 DCHECK(creation_phase_);
187 if (expr.
coeffs(
i) * multiplier == 0)
continue;
193 if (var_entries_.size() <= var)
return true;
195 absl::flat_hash_set<int> visited;
196 for (
const Entry& entry : var_entries_[var]) {
197 if (!visited.insert(entry.ct_index).second)
return false;
203 absl::Span<const int64_t>
solution) {
204 DCHECK(!creation_phase_);
207 activities_ = offsets_;
208 last_affected_variables_.ClearAndResize(columns_.size());
209 num_false_enforcement_.assign(num_constraints_, 0);
212 const int num_vars = columns_.size();
213 for (
int var = 0; var < num_vars; ++var) {
214 const SpanData& data = columns_[var];
215 const int64_t value =
solution[var];
217 if (value == 0 && data.num_pos_literal > 0) {
218 const int* ct_indices = &ct_buffer_[data.start];
219 for (
int k = 0; k < data.num_pos_literal; ++k) {
220 num_false_enforcement_[ct_indices[k]]++;
224 if (value == 1 && data.num_neg_literal > 0) {
225 const int* ct_indices = &ct_buffer_[data.start + data.num_pos_literal];
226 for (
int k = 0; k < data.num_neg_literal; ++k) {
227 num_false_enforcement_[ct_indices[k]]++;
231 if (value != 0 && data.num_linear_entries > 0) {
232 const int* ct_indices =
233 &ct_buffer_[data.start + data.num_pos_literal + data.num_neg_literal];
234 const int64_t* coeffs = &coeff_buffer_[data.linear_start];
235 for (
int k = 0; k < data.num_linear_entries; ++k) {
236 activities_[ct_indices[k]] += coeffs[k] * value;
242 for (
int c = 0; c < num_constraints_; ++c) {
243 distances_[c] = domains_[c].Distance(activities_[c]);
249 last_affected_variables_.ClearAndResize(columns_.size());
256 int c, absl::Span<const int64_t> jump_deltas,
257 absl::Span<double> var_to_score_change) {
258 if (c >= rows_.size())
return;
260 DCHECK_EQ(num_false_enforcement_[c], 0);
261 const SpanData& data = rows_[c];
266 const double enforcement_change =
static_cast<double>(-distances_[c]);
267 if (enforcement_change != 0.0) {
269 const int end = data.num_pos_literal + data.num_neg_literal;
271 for (
int k = 0; k <
end; ++k, ++
i) {
272 const int var = row_var_buffer_[
i];
273 if (!last_affected_variables_[var]) {
274 var_to_score_change[var] = enforcement_change;
275 last_affected_variables_.Set(var);
277 var_to_score_change[var] += enforcement_change;
283 if (data.num_linear_entries > 0) {
284 const int* row_vars = &row_var_buffer_[data.start + data.num_pos_literal +
285 data.num_neg_literal];
286 const int64_t* row_coeffs = &row_coeff_buffer_[data.linear_start];
287 num_ops_ += 2 * data.num_linear_entries;
292 const Domain& rhs = domains_[c];
293 const int64_t rhs_min = rhs.
Min();
294 const int64_t rhs_max = rhs.
Max();
296 const auto violation = [&rhs, rhs_min, rhs_max, is_simple](int64_t v) {
299 }
else if (v <= rhs_min) {
302 return is_simple ? int64_t{0} : rhs.
Distance(v);
306 const int64_t old_distance = distances_[c];
307 const int64_t activity = activities_[c];
308 for (
int k = 0; k < data.num_linear_entries; ++k) {
309 const int var = row_vars[k];
310 const int64_t coeff = row_coeffs[k];
312 violation(activity + coeff * jump_deltas[var]) - old_distance;
313 if (!last_affected_variables_[var]) {
314 var_to_score_change[var] =
static_cast<double>(diff);
315 last_affected_variables_.Set(var);
317 var_to_score_change[var] +=
static_cast<double>(diff);
323void LinearIncrementalEvaluator::UpdateScoreOnNewlyEnforced(
324 int c,
double weight, absl::Span<const int64_t> jump_deltas,
325 absl::Span<double> jump_scores) {
326 const SpanData& data = rows_[c];
330 const double weight_time_violation =
331 weight *
static_cast<double>(distances_[c]);
332 if (weight_time_violation > 0.0) {
334 const int end = data.num_pos_literal + data.num_neg_literal;
336 for (
int k = 0; k <
end; ++k, ++
i) {
337 const int var = row_var_buffer_[
i];
338 jump_scores[var] -= weight_time_violation;
339 last_affected_variables_.
Set(var);
345 int i = data.start + data.num_pos_literal + data.num_neg_literal;
346 int j = data.linear_start;
347 num_ops_ += 2 * data.num_linear_entries;
348 const int64_t old_distance = distances_[c];
349 for (
int k = 0; k < data.num_linear_entries; ++k, ++
i, ++j) {
350 const int var = row_var_buffer_[
i];
351 const int64_t coeff = row_coeff_buffer_[j];
352 const int64_t new_distance =
353 domains_[c].Distance(activities_[c] + coeff * jump_deltas[var]);
355 weight *
static_cast<double>(new_distance - old_distance);
356 last_affected_variables_.
Set(var);
361void LinearIncrementalEvaluator::UpdateScoreOnNewlyUnenforced(
362 int c,
double weight, absl::Span<const int64_t> jump_deltas,
363 absl::Span<double> jump_scores) {
364 const SpanData& data = rows_[
c];
369 const double weight_time_violation =
370 weight *
static_cast<double>(distances_[
c]);
371 if (weight_time_violation > 0.0) {
373 const int end = data.num_pos_literal + data.num_neg_literal;
375 for (
int k = 0; k <
end; ++k, ++
i) {
376 const int var = row_var_buffer_[
i];
377 jump_scores[var] += weight_time_violation;
383 int i = data.start + data.num_pos_literal + data.num_neg_literal;
384 int j = data.linear_start;
385 num_ops_ += 2 * data.num_linear_entries;
386 const int64_t old_distance = distances_[
c];
387 for (
int k = 0; k < data.num_linear_entries; ++k, ++
i, ++j) {
388 const int var = row_var_buffer_[
i];
389 const int64_t coeff = row_coeff_buffer_[j];
390 const int64_t new_distance =
391 domains_[
c].Distance(activities_[c] + coeff * jump_deltas[var]);
393 weight *
static_cast<double>(new_distance - old_distance);
394 last_affected_variables_.Set(var);
401void LinearIncrementalEvaluator::UpdateScoreOfEnforcementIncrease(
402 int c,
double score_change, absl::Span<const int64_t> jump_deltas,
403 absl::Span<double> jump_scores) {
404 if (score_change == 0.0)
return;
406 const SpanData& data = rows_[
c];
408 num_ops_ += data.num_pos_literal;
409 for (
int k = 0; k < data.num_pos_literal; ++k, ++
i) {
410 const int var = row_var_buffer_[
i];
411 if (jump_deltas[var] == 1) {
412 jump_scores[var] += score_change;
413 if (score_change < 0.0) {
414 last_affected_variables_.Set(var);
418 num_ops_ += data.num_neg_literal;
419 for (
int k = 0; k < data.num_neg_literal; ++k, ++
i) {
420 const int var = row_var_buffer_[
i];
421 if (jump_deltas[var] == -1) {
422 jump_scores[var] += score_change;
423 if (score_change < 0.0) {
424 last_affected_variables_.Set(var);
430void LinearIncrementalEvaluator::UpdateScoreOnActivityChange(
431 int c,
double weight, int64_t activity_delta,
432 absl::Span<const int64_t> jump_deltas, absl::Span<double> jump_scores) {
433 if (activity_delta == 0)
return;
434 const SpanData& data = rows_[
c];
445 const int64_t old_activity = activities_[
c];
446 const int64_t new_activity = old_activity + activity_delta;
449 if (new_activity > old_activity) {
450 min_range = old_activity - row_max_variations_[
c];
451 max_range = new_activity + row_max_variations_[
c];
453 min_range = new_activity - row_max_variations_[
c];
454 max_range = old_activity + row_max_variations_[
c];
458 if (Domain(min_range, max_range).IsIncludedIn(domains_[c]))
return;
464 static_cast<double>(domains_[
c].Distance(new_activity) - distances_[c]);
467 const int end = data.num_pos_literal + data.num_neg_literal;
469 for (
int k = 0; k <
end; ++k, ++
i) {
470 const int var = row_var_buffer_[
i];
471 jump_scores[var] += delta;
473 last_affected_variables_.Set(var);
481 if (min_range >= domains_[c].Max() || max_range <= domains_[c].Min())
return;
484 if (data.num_linear_entries > 0) {
485 const int* row_vars = &row_var_buffer_[data.start + data.num_pos_literal +
486 data.num_neg_literal];
487 const int64_t* row_coeffs = &row_coeff_buffer_[data.linear_start];
488 num_ops_ += 2 * data.num_linear_entries;
493 const Domain& rhs = domains_[
c];
494 const int64_t rhs_min = rhs.
Min();
495 const int64_t rhs_max = rhs.
Max();
496 const bool is_simple = rhs.NumIntervals() == 2;
497 const auto violation = [&rhs, rhs_min, rhs_max, is_simple](int64_t v) {
500 }
else if (v <= rhs_min) {
503 return is_simple ? int64_t{0} : rhs.Distance(v);
507 const int64_t old_a_minus_new_a =
508 distances_[
c] - domains_[
c].Distance(new_activity);
509 for (
int k = 0; k < data.num_linear_entries; ++k) {
510 const int var = row_vars[k];
511 const int64_t impact = row_coeffs[k] * jump_deltas[var];
512 const int64_t old_b = violation(old_activity + impact);
513 const int64_t new_b = violation(new_activity + impact);
520 const int64_t diff = old_a_minus_new_a + new_b - old_b;
525 jump_scores[var] += weight *
static_cast<double>(diff);
526 last_affected_variables_.Set(var);
533 int var, int64_t delta, absl::Span<const double> weights,
534 absl::Span<const int64_t> jump_deltas, absl::Span<double> jump_scores,
535 std::vector<int>* constraints_with_changed_violation) {
536 DCHECK(!creation_phase_);
538 if (var >= columns_.size())
return;
540 const SpanData& data = columns_[var];
542 num_ops_ += data.num_pos_literal;
543 for (
int k = 0; k < data.num_pos_literal; ++k, ++
i) {
544 const int c = ct_buffer_[
i];
547 num_false_enforcement_[c]--;
548 DCHECK_GE(num_false_enforcement_[c], 0);
549 if (num_false_enforcement_[c] == 0) {
550 UpdateScoreOnNewlyEnforced(c, weights[c], jump_deltas, jump_scores);
551 }
else if (num_false_enforcement_[c] == 1) {
552 const double enforcement_change =
553 weights[c] *
static_cast<double>(distances_[c]);
554 UpdateScoreOfEnforcementIncrease(c, enforcement_change, jump_deltas,
558 num_false_enforcement_[c]++;
559 if (num_false_enforcement_[c] == 1) {
560 UpdateScoreOnNewlyUnenforced(c, weights[c], jump_deltas, jump_scores);
561 }
else if (num_false_enforcement_[c] == 2) {
562 const double enforcement_change =
563 weights[c] *
static_cast<double>(distances_[c]);
564 UpdateScoreOfEnforcementIncrease(c, -enforcement_change, jump_deltas,
569 is_violated_[c] =
v1 > 0;
571 constraints_with_changed_violation->push_back(c);
574 num_ops_ += data.num_neg_literal;
575 for (
int k = 0; k < data.num_neg_literal; ++k, ++
i) {
576 const int c = ct_buffer_[
i];
579 num_false_enforcement_[c]--;
580 DCHECK_GE(num_false_enforcement_[c], 0);
581 if (num_false_enforcement_[c] == 0) {
582 UpdateScoreOnNewlyEnforced(c, weights[c], jump_deltas, jump_scores);
583 }
else if (num_false_enforcement_[c] == 1) {
584 const double enforcement_change =
585 weights[c] *
static_cast<double>(distances_[c]);
586 UpdateScoreOfEnforcementIncrease(c, enforcement_change, jump_deltas,
590 num_false_enforcement_[c]++;
591 if (num_false_enforcement_[c] == 1) {
592 UpdateScoreOnNewlyUnenforced(c, weights[c], jump_deltas, jump_scores);
593 }
else if (num_false_enforcement_[c] == 2) {
594 const double enforcement_change =
595 weights[c] *
static_cast<double>(distances_[c]);
596 UpdateScoreOfEnforcementIncrease(c, -enforcement_change, jump_deltas,
601 is_violated_[c] =
v1 > 0;
603 constraints_with_changed_violation->push_back(c);
606 int j = data.linear_start;
607 num_ops_ += 2 * data.num_linear_entries;
608 for (
int k = 0; k < data.num_linear_entries; ++k, ++
i, ++j) {
609 const int c = ct_buffer_[
i];
611 const int64_t coeff = coeff_buffer_[j];
613 if (num_false_enforcement_[c] == 1) {
617 const int64_t new_distance =
618 domains_[c].Distance(activities_[c] + coeff * delta);
619 if (new_distance != distances_[c]) {
620 UpdateScoreOfEnforcementIncrease(
621 c, -weights[c] *
static_cast<double>(distances_[c] - new_distance),
622 jump_deltas, jump_scores);
624 }
else if (num_false_enforcement_[c] == 0) {
625 UpdateScoreOnActivityChange(c, weights[c], coeff * delta, jump_deltas,
629 activities_[c] += coeff * delta;
630 distances_[c] = domains_[c].Distance(activities_[c]);
632 is_violated_[c] =
v1 > 0;
634 constraints_with_changed_violation->push_back(c);
640 return activities_[c];
644 return num_false_enforcement_[c] > 0 ? 0 : distances_[c];
648 DCHECK_EQ(is_violated_[c],
Violation(c) > 0);
649 return is_violated_[c];
653 if (domains_[c].Min() >= lb && domains_[c].Max() <= ub)
return false;
654 domains_[c] = domains_[c].IntersectionWith(
Domain(lb, ub));
655 distances_[c] = domains_[c].Distance(activities_[c]);
660 absl::Span<const double> weights)
const {
662 DCHECK_GE(weights.size(), num_constraints_);
663 for (
int c = 0; c < num_constraints_; ++c) {
664 if (num_false_enforcement_[c] > 0)
continue;
665 result += weights[c] *
static_cast<double>(distances_[c]);
676 absl::Span<const double> weights,
int var, int64_t delta)
const {
678 if (var >= columns_.size())
return 0.0;
679 const SpanData& data = columns_[var];
683 num_ops_ += data.num_pos_literal;
684 for (
int k = 0; k < data.num_pos_literal; ++k, ++
i) {
685 const int c = ct_buffer_[
i];
686 if (num_false_enforcement_[c] == 0) {
688 DCHECK_EQ(delta, -1);
689 result -= weights[c] *
static_cast<double>(distances_[c]);
691 if (delta == 1 && num_false_enforcement_[c] == 1) {
692 result += weights[c] *
static_cast<double>(distances_[c]);
697 num_ops_ += data.num_neg_literal;
698 for (
int k = 0; k < data.num_neg_literal; ++k, ++
i) {
699 const int c = ct_buffer_[
i];
700 if (num_false_enforcement_[c] == 0) {
703 result -= weights[c] *
static_cast<double>(distances_[c]);
705 if (delta == -1 && num_false_enforcement_[c] == 1) {
706 result += weights[c] *
static_cast<double>(distances_[c]);
711 int j = data.linear_start;
712 num_ops_ += 2 * data.num_linear_entries;
713 for (
int k = 0; k < data.num_linear_entries; ++k, ++
i, ++j) {
714 const int c = ct_buffer_[
i];
715 if (num_false_enforcement_[c] > 0)
continue;
716 const int64_t coeff = coeff_buffer_[j];
717 const int64_t old_distance = distances_[c];
718 const int64_t new_distance =
719 domains_[c].Distance(activities_[c] + coeff * delta);
720 result += weights[c] *
static_cast<double>(new_distance - old_distance);
727 if (var >= columns_.size())
return false;
728 for (
const int c : VarToConstraints(var)) {
735 int var, int64_t current_value,
const Domain& var_domain)
const {
737 if (var_domain.
Size() <= 2 || var >= columns_.size())
return result;
739 const SpanData& data = columns_[var];
740 int i = data.start + data.num_pos_literal + data.num_neg_literal;
741 int j = data.linear_start;
742 for (
int k = 0; k < data.num_linear_entries; ++k, ++
i, ++j) {
743 const int c = ct_buffer_[
i];
744 if (num_false_enforcement_[c] > 0)
continue;
749 const int64_t coeff = coeff_buffer_[j];
750 const int64_t activity = activities_[c] - current_value * coeff;
752 const int64_t slack_min =
CapSub(domains_[c].Min(), activity);
753 const int64_t slack_max =
CapSub(domains_[c].Max(), activity);
754 if (slack_min != std::numeric_limits<int64_t>::min()) {
756 if (ceil_bp != result.back() && var_domain.
Contains(ceil_bp)) {
757 result.push_back(ceil_bp);
760 if (floor_bp != result.back() && var_domain.
Contains(floor_bp)) {
761 result.push_back(floor_bp);
764 if (slack_min != slack_max &&
765 slack_max != std::numeric_limits<int64_t>::min()) {
767 if (ceil_bp != result.back() && var_domain.
Contains(ceil_bp)) {
768 result.push_back(ceil_bp);
771 if (floor_bp != result.back() && var_domain.
Contains(floor_bp)) {
772 result.push_back(floor_bp);
782 absl::Span<const int64_t> var_max_variation) {
783 creation_phase_ =
false;
784 if (num_constraints_ == 0)
return;
789 int total_linear_size = 0;
790 tmp_row_sizes_.assign(num_constraints_, 0);
791 tmp_row_num_positive_literals_.assign(num_constraints_, 0);
792 tmp_row_num_negative_literals_.assign(num_constraints_, 0);
793 tmp_row_num_linear_entries_.assign(num_constraints_, 0);
794 for (
const auto& column : literal_entries_) {
795 total_size += column.size();
796 for (
const auto [c, is_positive] : column) {
799 tmp_row_num_positive_literals_[c]++;
801 tmp_row_num_negative_literals_[c]++;
806 row_max_variations_.assign(num_constraints_, 0);
807 for (
int var = 0; var < var_entries_.size(); ++var) {
808 const int64_t range = var_max_variation[var];
809 const auto& column = var_entries_[var];
810 total_size += column.size();
811 total_linear_size += column.size();
812 for (
const auto [c, coeff] : column) {
814 tmp_row_num_linear_entries_[c]++;
815 row_max_variations_[c] =
816 std::max(row_max_variations_[c], range * std::abs(coeff));
821 ct_buffer_.reserve(total_size);
822 coeff_buffer_.reserve(total_linear_size);
823 columns_.resize(std::max(literal_entries_.size(), var_entries_.size()));
824 for (
int var = 0; var < columns_.size(); ++var) {
825 columns_[var].start =
static_cast<int>(ct_buffer_.size());
826 columns_[var].linear_start =
static_cast<int>(coeff_buffer_.size());
827 if (var < literal_entries_.size()) {
828 for (
const auto [c, is_positive] : literal_entries_[var]) {
830 columns_[var].num_pos_literal++;
831 ct_buffer_.push_back(c);
834 for (
const auto [c, is_positive] : literal_entries_[var]) {
836 columns_[var].num_neg_literal++;
837 ct_buffer_.push_back(c);
841 if (var < var_entries_.size()) {
842 for (
const auto [c, coeff] : var_entries_[var]) {
843 columns_[var].num_linear_entries++;
844 ct_buffer_.push_back(c);
845 coeff_buffer_.push_back(coeff);
861 int linear_offset = 0;
862 rows_.resize(num_constraints_);
863 for (
int c = 0; c < num_constraints_; ++c) {
864 rows_[c].num_pos_literal = tmp_row_num_positive_literals_[c];
865 rows_[c].num_neg_literal = tmp_row_num_negative_literals_[c];
866 rows_[c].num_linear_entries = tmp_row_num_linear_entries_[c];
868 rows_[c].start = offset;
869 offset += tmp_row_sizes_[c];
870 tmp_row_sizes_[c] = rows_[c].start;
872 rows_[c].linear_start = linear_offset;
873 linear_offset += tmp_row_num_linear_entries_[c];
874 tmp_row_num_linear_entries_[c] = rows_[c].linear_start;
876 DCHECK_EQ(offset, total_size);
877 DCHECK_EQ(linear_offset, total_linear_size);
880 row_var_buffer_.resize(total_size);
881 row_coeff_buffer_.resize(total_linear_size);
882 for (
int var = 0; var < columns_.size(); ++var) {
883 const SpanData& data = columns_[var];
885 for (
int k = 0; k < data.num_pos_literal; ++
i, ++k) {
886 const int c = ct_buffer_[
i];
887 row_var_buffer_[tmp_row_sizes_[c]++] = var;
890 for (
int var = 0; var < columns_.size(); ++var) {
891 const SpanData& data = columns_[var];
892 int i = data.start + data.num_pos_literal;
893 for (
int k = 0; k < data.num_neg_literal; ++
i, ++k) {
894 const int c = ct_buffer_[
i];
895 row_var_buffer_[tmp_row_sizes_[c]++] = var;
898 for (
int var = 0; var < columns_.size(); ++var) {
899 const SpanData& data = columns_[var];
900 int i = data.start + data.num_pos_literal + data.num_neg_literal;
901 int j = data.linear_start;
902 for (
int k = 0; k < data.num_linear_entries; ++
i, ++j, ++k) {
903 const int c = ct_buffer_[
i];
904 row_var_buffer_[tmp_row_sizes_[c]++] = var;
905 row_coeff_buffer_[tmp_row_num_linear_entries_[c]++] = coeff_buffer_[j];
909 cached_deltas_.assign(columns_.size(), 0);
910 cached_scores_.assign(columns_.size(), 0);
911 last_affected_variables_.ClearAndResize(columns_.size());
915 for (
const int c : VarToConstraints(var)) {
916 if (domains_[c].NumIntervals() > 2)
return false;
924 absl::Span<const int64_t>
solution) {
929 int var, int64_t old_value,
930 absl::Span<const int64_t> solution_with_new_value) {
935 absl::Span<const int64_t>
solution) {
951 result.push_back(var);
955 result.shrink_to_fit();
966 absl::Span<const int64_t>
solution) {
967 int64_t sum_of_literals = 0;
968 for (
const int lit :
ct_proto().bool_xor().literals()) {
969 sum_of_literals += LiteralValue(lit,
solution);
971 return 1 - (sum_of_literals % 2);
976 absl::Span<const int64_t> ) {
987 absl::Span<const int64_t>
solution) {
988 const int64_t target_value =
990 int64_t max_of_expressions = std::numeric_limits<int64_t>::min();
992 const int64_t expr_value = ExprValue(expr,
solution);
993 max_of_expressions = std::max(max_of_expressions, expr_value);
995 return std::max(target_value - max_of_expressions, int64_t{0});
1005 absl::Span<const int64_t>
solution) {
1006 const int64_t target_value =
1008 int64_t prod_value = 1;
1010 prod_value *= ExprValue(expr,
solution);
1012 return std::abs(target_value - prod_value);
1022 absl::Span<const int64_t>
solution) {
1023 const int64_t target_value =
1025 DCHECK_EQ(
ct_proto().int_div().exprs_size(), 2);
1026 const int64_t div_value = ExprValue(
ct_proto().int_div().exprs(0),
solution) /
1028 return std::abs(target_value - div_value);
1038 absl::Span<const int64_t>
solution) {
1039 const int64_t target_value =
1041 DCHECK_EQ(
ct_proto().int_mod().exprs_size(), 2);
1043 const int64_t expr_value = ExprValue(
ct_proto().int_mod().exprs(0),
solution);
1044 const int64_t mod_value = ExprValue(
ct_proto().int_mod().exprs(1),
solution);
1045 const int64_t rhs = expr_value % mod_value;
1046 if ((expr_value >= 0 && target_value >= 0) ||
1047 (expr_value <= 0 && target_value <= 0)) {
1049 return std::min({std::abs(target_value - rhs),
1050 std::abs(target_value) + std::abs(mod_value - rhs),
1051 std::abs(rhs) + std::abs(mod_value - target_value)});
1056 return std::abs(target_value) + std::abs(expr_value);
1067 absl::Span<const int64_t>
solution) {
1070 values_.push_back(ExprValue(expr,
solution));
1072 std::sort(values_.begin(), values_.end());
1074 int64_t value = values_[0];
1077 for (
int i = 1;
i < values_.size(); ++
i) {
1078 const int64_t new_value = values_[
i];
1079 if (new_value == value) {
1082 violation += counter * (counter - 1) / 2;
1087 violation += counter * (counter - 1) / 2;
1093template <
bool has_enforcement>
1095 int , int64_t , absl::Span<const int64_t>
solution) {
1096 if (has_enforcement) {
1097 for (
const int lit : enforcements_) {
1102 const int64_t s1 = AffineValue(interval1_.start,
solution);
1103 const int64_t e1 = AffineValue(interval1_.end,
solution);
1104 const int64_t s2 = AffineValue(interval2_.start,
solution);
1105 const int64_t e2 = AffineValue(interval2_.end,
solution);
1106 const int64_t repair = std::min(e2 - s1, e1 - s2);
1111template <
bool has_enforcement>
1115 std::vector<int> result;
1116 if (has_enforcement) {
1117 for (
const int ref : enforcements_) result.push_back(
PositiveRef(ref));
1119 interval1_.start.AppendVarTo(result);
1120 interval1_.end.AppendVarTo(result);
1121 interval2_.start.AppendVarTo(result);
1122 interval2_.end.AppendVarTo(result);
1124 result.shrink_to_fit();
1132 absl::Span<const int64_t>
solution) {
1134 if (!LiteralValue(lit,
solution))
return 0;
1137 if (!LiteralValue(lit,
solution))
return 0;
1146 if (start1 >= end2 || start2 >= end1)
return 0;
1150 return std::max(std::min(std::min(end2 - start2, end1 - start1),
1151 std::min(end2 - start1, end1 - start2)),
1157 absl::Span<const int64_t>
solution) {
1159 if (!LiteralValue(lit,
solution))
return 0;
1162 if (!LiteralValue(lit,
solution))
return 0;
1171 return std::max(std::min(end2 - start1, end1 - start2), int64_t{0});
1179 absl::Span<const int64_t>
solution) {
1180 DCHECK_GE(
ct_proto().no_overlap_2d().x_intervals_size(), 2);
1184 for (
int i = 0;
i + 1 < size; ++
i) {
1186 cp_model_.constraints(
ct_proto().no_overlap_2d().x_intervals(
i));
1188 cp_model_.constraints(
ct_proto().no_overlap_2d().y_intervals(
i));
1189 for (
int j =
i + 1; j < size; ++j) {
1191 cp_model_.constraints(
ct_proto().no_overlap_2d().x_intervals(j));
1193 cp_model_.constraints(
ct_proto().no_overlap_2d().y_intervals(j));
1212template <
bool has_enforcement>
1214 int , int64_t , absl::Span<const int64_t>
solution) {
1215 if (has_enforcement) {
1216 for (
const int lit : enforcements_) {
1221 const int64_t x1 = AffineValue(box1_.x_min,
solution);
1222 const int64_t X1 = AffineValue(box1_.x_max,
solution);
1223 const int64_t x2 = AffineValue(box2_.x_min,
solution);
1224 const int64_t X2 = AffineValue(box2_.x_max,
solution);
1225 const int64_t repair_x = std::min(X2 - x1, X1 - x2);
1228 const int64_t y1 = AffineValue(box1_.y_min,
solution);
1229 const int64_t Y1 = AffineValue(box1_.y_max,
solution);
1230 const int64_t y2 = AffineValue(box2_.y_min,
solution);
1231 const int64_t Y2 = AffineValue(box2_.y_max,
solution);
1232 const int64_t repair_y = std::min(Y2 - y1, Y1 - y2);
1235 const int64_t overlap_x =
1236 std::min(std::max(std::min(X2 - x2, X1 - x1), int64_t{1}), repair_x);
1237 const int64_t overlap_y =
1238 std::min(std::max(std::min(Y2 - y2, Y1 - y1), int64_t{1}), repair_y);
1239 return std::min(repair_x * overlap_y, repair_y * overlap_x) -
violation_;
1242template <
bool has_enforcement>
1246 std::vector<int> result;
1247 if (has_enforcement) {
1248 for (
const int ref : enforcements_) result.push_back(
PositiveRef(ref));
1250 box1_.x_min.AppendVarTo(result);
1251 box1_.x_max.AppendVarTo(result);
1252 box1_.y_min.AppendVarTo(result);
1253 box1_.y_max.AppendVarTo(result);
1254 box2_.x_min.AppendVarTo(result);
1255 box2_.x_max.AppendVarTo(result);
1256 box2_.y_min.AppendVarTo(result);
1257 box2_.y_max.AppendVarTo(result);
1259 result.shrink_to_fit();
1282 absl::Span<const int64_t> new_solution)
override;
1284 int var, int64_t old_value,
1285 absl::Span<const int64_t> solution_with_new_value)
override;
1289 void emplace_back(
const int* start,
const int*
end);
1290 void reset(
int num_nodes);
1292 int num_components = 0;
1293 std::vector<bool> skipped;
1294 std::vector<int> root;
1296 void InitGraph(absl::Span<const int64_t>
solution);
1297 bool UpdateGraph(
int var, int64_t value);
1298 int64_t ViolationForCurrentGraph();
1300 absl::flat_hash_map<int, std::vector<int>> arcs_by_lit_;
1301 absl::Span<const int> literals_;
1302 absl::Span<const int> tails_;
1303 absl::Span<const int> heads_;
1305 std::vector<DenseSet<int>> graph_;
1307 SccOutput committed_sccs_;
1308 std::vector<bool> has_in_arc_;
1313void CompiledCircuitConstraint::SccOutput::emplace_back(
int const* start,
1315 const int root_node = *start;
1316 const int size =
end - start;
1320 for (; start !=
end; ++start) {
1321 root[*start] = root_node;
1322 skipped[*start] = (size == 1);
1325void CompiledCircuitConstraint::SccOutput::reset(
int num_nodes) {
1328 root.resize(num_nodes);
1330 skipped.resize(num_nodes);
1336 const bool routes =
ct_proto.has_routes();
1338 heads_ = absl::MakeConstSpan(routes ?
ct_proto.routes().heads()
1340 literals_ = absl::MakeConstSpan(routes ?
ct_proto.routes().literals()
1342 graph_.resize(*absl::c_max_element(tails_) + 1);
1343 for (
int i = 0;
i < literals_.size(); ++
i) {
1344 arcs_by_lit_[literals_[
i]].push_back(
i);
1348void CompiledCircuitConstraint::InitGraph(absl::Span<const int64_t>
solution) {
1352 for (
int i = 0;
i < tails_.size(); ++
i) {
1353 if (!LiteralValue(literals_[
i],
solution))
continue;
1354 graph_[tails_[
i]].
insert(heads_[
i]);
1358bool CompiledCircuitConstraint::UpdateGraph(
int var, int64_t value) {
1359 bool needs_update =
false;
1360 const int enabled_lit =
1362 const int disabled_lit =
NegatedRef(enabled_lit);
1363 for (
const int arc : arcs_by_lit_[disabled_lit]) {
1364 const int tail = tails_[arc];
1365 const int head = heads_[arc];
1367 needs_update = needs_update || tail != head;
1368 graph_[tails_[arc]].erase(heads_[arc]);
1370 for (
const int arc : arcs_by_lit_[enabled_lit]) {
1371 const int tail = tails_[arc];
1372 const int head = heads_[arc];
1374 needs_update = needs_update ||
1375 committed_sccs_.root[tail] != committed_sccs_.root[head];
1376 graph_[tails_[arc]].insert(heads_[arc]);
1378 return needs_update;
1382 int var, int64_t, absl::Span<const int64_t> new_solution) {
1383 UpdateGraph(var, new_solution[var]);
1385 std::swap(committed_sccs_, sccs_);
1389 absl::Span<const int64_t>
solution) {
1391 int64_t result = ViolationForCurrentGraph();
1392 std::swap(committed_sccs_, sccs_);
1397 int var, int64_t old_value,
1398 absl::Span<const int64_t> solution_with_new_value) {
1400 if (UpdateGraph(var, solution_with_new_value[var])) {
1401 result = ViolationForCurrentGraph() -
violation_;
1403 UpdateGraph(var, old_value);
1407int64_t CompiledCircuitConstraint::ViolationForCurrentGraph() {
1408 const int num_nodes = graph_.
size();
1409 sccs_.reset(num_nodes);
1410 scc_finder_.FindStronglyConnectedComponents(num_nodes, graph_, &sccs_);
1413 if (sccs_.num_components == 0)
return 0;
1416 int num_half_connected_components = 0;
1417 has_in_arc_.clear();
1418 has_in_arc_.resize(num_nodes,
false);
1419 for (
int tail = 0; tail < graph_.
size(); ++tail) {
1420 if (sccs_.skipped[tail])
continue;
1421 for (
const int head : graph_[tail]) {
1422 const int head_root = sccs_.root[head];
1423 if (sccs_.root[tail] == head_root)
continue;
1424 if (has_in_arc_[head_root])
continue;
1425 if (sccs_.skipped[head_root])
continue;
1426 has_in_arc_[head_root] =
true;
1427 ++num_half_connected_components;
1430 const int64_t
violation = sccs_.num_components - 1 + sccs_.num_components -
1431 num_half_connected_components - 1 +
1433 VLOG(2) <<
"#SCCs=" << sccs_.num_components <<
" #nodes=" << num_nodes
1434 <<
" #half_connected_components=" << num_half_connected_components
1447 std::vector<std::vector<int>> inflow_lits;
1448 std::vector<std::vector<int>> outflow_lits;
1449 for (
int i = 0;
i < heads.size(); ++
i) {
1450 if (heads[
i] >= inflow_lits.size()) {
1451 inflow_lits.resize(heads[
i] + 1);
1453 inflow_lits[heads[
i]].push_back(literals[
i]);
1454 if (tails[
i] >= outflow_lits.size()) {
1455 outflow_lits.resize(tails[
i] + 1);
1457 outflow_lits[tails[
i]].push_back(literals[
i]);
1460 const int depot_net_flow = linear_evaluator.
NewConstraint({0, 0});
1461 for (
const int lit : inflow_lits[0]) {
1462 linear_evaluator.
AddLiteral(depot_net_flow, lit, 1);
1464 for (
const int lit : outflow_lits[0]) {
1465 linear_evaluator.
AddLiteral(depot_net_flow, lit, -1);
1468 for (
int i = routes ? 1 : 0;
i < inflow_lits.size(); ++
i) {
1469 const int inflow_ct = linear_evaluator.
NewConstraint({1, 1});
1470 for (
const int lit : inflow_lits[
i]) {
1474 for (
int i = routes ? 1 : 0;
i < outflow_lits.size(); ++
i) {
1475 const int outflow_ct = linear_evaluator.
NewConstraint({1, 1});
1476 for (
const int lit : outflow_lits[
i]) {
1477 linear_evaluator.
AddLiteral(outflow_ct, lit);
1486 : cp_model_(cp_model), params_(params), time_limit_(
time_limit) {
1487 var_to_constraints_.resize(cp_model_.variables_size());
1488 var_to_dtime_estimate_.resize(cp_model_.variables_size());
1489 jump_value_optimal_.resize(cp_model_.variables_size(),
true);
1490 num_violated_constraint_per_var_ignoring_objective_.assign(
1491 cp_model_.variables_size(), 0);
1493 std::vector<bool> ignored_constraints(cp_model_.constraints_size(),
false);
1494 std::vector<ConstraintProto> additional_constraints;
1495 CompileConstraintsAndObjective(ignored_constraints, additional_constraints);
1496 BuildVarConstraintGraph();
1502 const std::vector<bool>& ignored_constraints,
1503 const std::vector<ConstraintProto>& additional_constraints,
1505 : cp_model_(cp_model), params_(params), time_limit_(
time_limit) {
1506 var_to_constraints_.resize(cp_model_.variables_size());
1507 var_to_dtime_estimate_.resize(cp_model_.variables_size());
1508 jump_value_optimal_.resize(cp_model_.variables_size(),
true);
1509 num_violated_constraint_per_var_ignoring_objective_.assign(
1510 cp_model_.variables_size(), 0);
1511 CompileConstraintsAndObjective(ignored_constraints, additional_constraints);
1512 BuildVarConstraintGraph();
1516void LsEvaluator::BuildVarConstraintGraph() {
1518 for (std::vector<int>& ct_indices : var_to_constraints_) ct_indices.clear();
1519 constraint_to_vars_.resize(constraints_.size());
1522 for (
int ct_index = 0; ct_index < constraints_.size(); ++ct_index) {
1523 constraint_to_vars_[ct_index] =
1524 constraints_[ct_index]->UsedVariables(cp_model_);
1526 const double dtime = 1e-8 * constraint_to_vars_[ct_index].size();
1527 for (
const int var : constraint_to_vars_[ct_index]) {
1528 var_to_constraints_[var].push_back(ct_index);
1529 var_to_dtime_estimate_[var] += dtime;
1534 for (std::vector<int>& constraints : var_to_constraints_) {
1537 for (std::vector<int>& vars : constraint_to_vars_) {
1542 jump_value_optimal_.resize(cp_model_.variables_size());
1543 for (
int i = 0;
i < cp_model_.variables_size(); ++
i) {
1544 if (!var_to_constraints_[
i].empty()) {
1545 jump_value_optimal_[
i] =
false;
1549 const IntegerVariableProto& var_proto = cp_model_.variables(
i);
1550 if (var_proto.domain_size() == 2 && var_proto.domain(0) == 0 &&
1551 var_proto.domain(1) == 1) {
1553 jump_value_optimal_[
i] =
true;
1557 jump_value_optimal_[
i] = linear_evaluator_.ViolationChangeIsConvex(
i);
1562 switch (ct.constraint_case()) {
1565 const int ct_index = linear_evaluator_.NewConstraint(Domain(1, 1));
1566 for (
const int lit : ct.enforcement_literal()) {
1567 linear_evaluator_.AddEnforcementLiteral(ct_index, lit);
1569 for (
const int lit : ct.bool_or().literals()) {
1570 linear_evaluator_.AddEnforcementLiteral(ct_index,
NegatedRef(lit));
1575 const int num_literals = ct.bool_and().literals_size();
1576 const int ct_index =
1577 linear_evaluator_.NewConstraint(Domain(num_literals));
1578 for (
const int lit : ct.enforcement_literal()) {
1579 linear_evaluator_.AddEnforcementLiteral(ct_index, lit);
1581 for (
const int lit : ct.bool_and().literals()) {
1582 linear_evaluator_.AddLiteral(ct_index, lit);
1587 DCHECK(ct.enforcement_literal().empty());
1588 const int ct_index = linear_evaluator_.NewConstraint({0, 1});
1589 for (
const int lit : ct.at_most_one().literals()) {
1590 linear_evaluator_.AddLiteral(ct_index, lit);
1595 DCHECK(ct.enforcement_literal().empty());
1596 const int ct_index = linear_evaluator_.NewConstraint({1, 1});
1597 for (
const int lit : ct.exactly_one().literals()) {
1598 linear_evaluator_.AddLiteral(ct_index, lit);
1603 constraints_.emplace_back(
new CompiledBoolXorConstraint(ct));
1607 constraints_.emplace_back(
new CompiledAllDiffConstraint(ct));
1613 const LinearExpressionProto& target = ct.lin_max().target();
1614 for (
const LinearExpressionProto& expr : ct.lin_max().exprs()) {
1615 const int64_t max_value =
1616 ExprMax(target, cp_model_) - ExprMin(expr, cp_model_);
1617 const int precedence_index =
1618 linear_evaluator_.NewConstraint({0, max_value});
1619 linear_evaluator_.AddLinearExpression(precedence_index, target, 1);
1620 linear_evaluator_.AddLinearExpression(precedence_index, expr, -1);
1624 constraints_.emplace_back(
new CompiledLinMaxConstraint(ct));
1628 constraints_.emplace_back(
new CompiledIntProdConstraint(ct));
1632 constraints_.emplace_back(
new CompiledIntDivConstraint(ct));
1636 DCHECK_EQ(ExprMin(ct.int_mod().exprs(1), cp_model_),
1637 ExprMax(ct.int_mod().exprs(1), cp_model_));
1638 constraints_.emplace_back(
new CompiledIntModConstraint(ct));
1643 const int ct_index = linear_evaluator_.NewConstraint(domain);
1644 for (
const int lit : ct.enforcement_literal()) {
1645 linear_evaluator_.AddEnforcementLiteral(ct_index, lit);
1647 for (
int i = 0;
i < ct.linear().vars_size(); ++
i) {
1648 const int var = ct.linear().vars(
i);
1649 const int64_t coeff = ct.linear().coeffs(
i);
1650 linear_evaluator_.AddTerm(ct_index, var, coeff);
1655 const int size = ct.no_overlap().intervals_size();
1656 if (size <= 1)
break;
1657 if (size > params_.feasibility_jump_max_expanded_constraint_size()) {
1660 LinearExpressionProto one;
1662 std::vector<std::optional<int>> is_active;
1663 std::vector<LinearExpressionProto> times;
1664 std::vector<LinearExpressionProto> demands;
1665 const int num_intervals = ct.no_overlap().intervals().size();
1666 for (
int i = 0;
i < num_intervals; ++
i) {
1667 const ConstraintProto& interval_ct =
1668 cp_model_.constraints(ct.no_overlap().intervals(
i));
1669 if (interval_ct.enforcement_literal().empty()) {
1670 is_active.push_back(std::nullopt);
1671 is_active.push_back(std::nullopt);
1673 CHECK_EQ(interval_ct.enforcement_literal().size(), 1);
1674 is_active.push_back(interval_ct.enforcement_literal(0));
1675 is_active.push_back(interval_ct.enforcement_literal(0));
1678 times.push_back(interval_ct.interval().start());
1679 times.push_back(LinearExprSum(interval_ct.interval().start(),
1680 interval_ct.interval().size()));
1681 demands.push_back(one);
1682 demands.push_back(NegatedLinearExpression(one));
1684 constraints_.emplace_back(
new CompiledReservoirConstraint(
1685 std::move(one), std::move(is_active), std::move(times),
1686 std::move(demands)));
1690 for (
int i = 0;
i + 1 < size; ++
i) {
1691 const ConstraintProto& proto_i =
1692 cp_model_.constraints(ct.no_overlap().intervals(
i));
1693 const IntervalConstraintProto& interval_i = proto_i.interval();
1694 const int64_t min_start_i = ExprMin(interval_i.start(), cp_model_);
1695 const int64_t max_end_i = ExprMax(interval_i.end(), cp_model_);
1696 for (
int j =
i + 1; j < size; ++j) {
1697 const ConstraintProto& proto_j =
1698 cp_model_.constraints(ct.no_overlap().intervals(j));
1699 const IntervalConstraintProto& interval_j = proto_j.interval();
1700 const int64_t min_start_j = ExprMin(interval_j.start(), cp_model_);
1701 const int64_t max_end_j = ExprMax(interval_j.end(), cp_model_);
1702 if (min_start_i >= max_end_j || min_start_j >= max_end_i)
continue;
1704 const bool has_enforcement =
1705 !proto_i.enforcement_literal().empty() ||
1706 !proto_j.enforcement_literal().empty();
1707 if (has_enforcement) {
1708 constraints_.emplace_back(
1709 new CompiledNoOverlapWithTwoIntervals<true>(proto_i,
1712 constraints_.emplace_back(
1713 new CompiledNoOverlapWithTwoIntervals<false>(proto_i,
1722 LinearExpressionProto capacity = ct.cumulative().capacity();
1723 std::vector<std::optional<int>> is_active;
1724 std::vector<LinearExpressionProto> times;
1725 std::vector<LinearExpressionProto> demands;
1726 const int num_intervals = ct.cumulative().intervals().size();
1727 for (
int i = 0;
i < num_intervals; ++
i) {
1728 const ConstraintProto& interval_ct =
1729 cp_model_.constraints(ct.cumulative().intervals(
i));
1730 if (interval_ct.enforcement_literal().empty()) {
1731 is_active.push_back(std::nullopt);
1732 is_active.push_back(std::nullopt);
1734 CHECK_EQ(interval_ct.enforcement_literal().size(), 1);
1735 is_active.push_back(interval_ct.enforcement_literal(0));
1736 is_active.push_back(interval_ct.enforcement_literal(0));
1740 times.push_back(interval_ct.interval().start());
1741 demands.push_back(ct.cumulative().demands(
i));
1751 times.push_back(LinearExprSum(interval_ct.interval().start(),
1752 interval_ct.interval().size()));
1753 demands.push_back(NegatedLinearExpression(ct.cumulative().demands(
i)));
1756 constraints_.emplace_back(
new CompiledReservoirConstraint(
1757 std::move(capacity), std::move(is_active), std::move(times),
1758 std::move(demands)));
1762 const auto& x_intervals = ct.no_overlap_2d().x_intervals();
1763 const auto& y_intervals = ct.no_overlap_2d().y_intervals();
1764 const int size = x_intervals.size();
1765 if (size <= 1)
break;
1767 size > params_.feasibility_jump_max_expanded_constraint_size()) {
1768 CompiledNoOverlap2dConstraint* no_overlap_2d =
1769 new CompiledNoOverlap2dConstraint(ct, cp_model_);
1770 constraints_.emplace_back(no_overlap_2d);
1774 for (
int i = 0;
i + 1 < size; ++
i) {
1775 const ConstraintProto& x_proto_i =
1776 cp_model_.constraints(x_intervals[
i]);
1777 const IntervalConstraintProto& x_interval_i = x_proto_i.interval();
1778 const int64_t x_min_start_i = ExprMin(x_interval_i.start(), cp_model_);
1779 const int64_t x_max_end_i = ExprMax(x_interval_i.end(), cp_model_);
1780 const ConstraintProto& y_proto_i =
1781 cp_model_.constraints(y_intervals[
i]);
1782 const IntervalConstraintProto& y_interval_i = y_proto_i.interval();
1783 const int64_t y_min_start_i = ExprMin(y_interval_i.start(), cp_model_);
1784 const int64_t y_max_end_i = ExprMax(y_interval_i.end(), cp_model_);
1785 for (
int j =
i + 1; j < size; ++j) {
1786 const ConstraintProto& x_proto_j =
1787 cp_model_.constraints(x_intervals[j]);
1788 const IntervalConstraintProto& x_interval_j = x_proto_j.interval();
1789 const int64_t x_min_start_j =
1790 ExprMin(x_interval_j.start(), cp_model_);
1791 const int64_t x_max_end_j = ExprMax(x_interval_j.end(), cp_model_);
1792 const ConstraintProto& y_proto_j =
1793 cp_model_.constraints(y_intervals[j]);
1794 const IntervalConstraintProto& y_interval_j = y_proto_j.interval();
1795 const int64_t y_min_start_j =
1796 ExprMin(y_interval_j.start(), cp_model_);
1797 const int64_t y_max_end_j = ExprMax(y_interval_j.end(), cp_model_);
1798 if (x_min_start_i >= x_max_end_j || x_min_start_j >= x_max_end_i ||
1799 y_min_start_i >= y_max_end_j || y_min_start_j >= y_max_end_i) {
1803 const bool has_enforcement =
1804 !x_proto_i.enforcement_literal().empty() ||
1805 !x_proto_j.enforcement_literal().empty() ||
1806 !y_proto_i.enforcement_literal().empty() ||
1807 !y_proto_j.enforcement_literal().empty();
1808 if (has_enforcement) {
1809 constraints_.emplace_back(
new CompiledNoOverlap2dWithTwoBoxes<true>(
1810 x_proto_i, y_proto_i, x_proto_j, y_proto_j));
1812 constraints_.emplace_back(
1813 new CompiledNoOverlap2dWithTwoBoxes<false>(
1814 x_proto_i, y_proto_i, x_proto_j, y_proto_j));
1822 constraints_.emplace_back(
new CompiledCircuitConstraint(ct));
1826 VLOG(1) <<
"Not implemented: " << ct.constraint_case();
1831void LsEvaluator::CompileConstraintsAndObjective(
1832 const std::vector<bool>& ignored_constraints,
1833 const std::vector<ConstraintProto>& additional_constraints) {
1834 constraints_.clear();
1837 if (cp_model_.has_objective()) {
1838 const int ct_index = linear_evaluator_.NewConstraint(
1839 cp_model_.objective().domain().empty()
1842 DCHECK_EQ(0, ct_index);
1843 for (
int i = 0;
i < cp_model_.objective().vars_size(); ++
i) {
1844 const int var = cp_model_.objective().vars(
i);
1845 const int64_t coeff = cp_model_.objective().coeffs(
i);
1846 linear_evaluator_.AddTerm(ct_index, var, coeff);
1850 TimeLimitCheckEveryNCalls checker(1000, time_limit_);
1851 for (
int c = 0;
c < cp_model_.constraints_size(); ++
c) {
1852 if (ignored_constraints[c])
continue;
1853 CompileOneConstraint(cp_model_.constraints(c));
1854 if (checker.LimitReached())
break;
1857 for (
const ConstraintProto& ct : additional_constraints) {
1858 CompileOneConstraint(ct);
1862 std::vector<int64_t> var_max_variations(cp_model_.variables().size());
1863 for (
int var = 0; var < cp_model_.variables().size(); ++var) {
1864 const auto& domain = cp_model_.variables(var).domain();
1865 var_max_variations[var] = domain[domain.size() - 1] - domain[0];
1867 linear_evaluator_.PrecomputeCompactView(var_max_variations);
1871 if (!cp_model_.has_objective())
return false;
1872 if (linear_evaluator_.ReduceBounds(0, lb, ub)) {
1881 linear_evaluator_.ComputeInitialActivities(
solution);
1884 for (
const auto& ct : constraints_) {
1892 absl::Span<const int64_t>
solution) {
1894 for (
const auto& ct : constraints_) {
1900 int var, int64_t old_value, absl::Span<const int64_t> new_solution) {
1901 for (
const int general_ct_index : var_to_constraints_[var]) {
1902 const int c = general_ct_index + linear_evaluator_.num_constraints();
1903 const int64_t v0 = constraints_[general_ct_index]->violation();
1904 constraints_[general_ct_index]->PerformMove(var, old_value, new_solution);
1905 const int64_t violation_delta =
1906 constraints_[general_ct_index]->violation() - v0;
1907 if (violation_delta != 0) {
1908 last_update_violation_changes_.push_back(c);
1915 absl::Span<const double> weights,
1916 absl::Span<const int64_t> jump_deltas,
1917 absl::Span<double> jump_scores) {
1919 if (old_value == new_value)
return;
1920 last_update_violation_changes_.clear();
1921 linear_evaluator_.ClearAffectedVariables();
1922 linear_evaluator_.UpdateVariableAndScores(var, new_value - old_value, weights,
1923 jump_deltas, jump_scores,
1924 &last_update_violation_changes_);
1929 dtime_ += 1e-8 * last_update_violation_changes_.size();
1930 for (
const int c : last_update_violation_changes_) {
1936 int64_t evaluation = 0;
1939 for (
int i = 0;
i < linear_evaluator_.num_constraints(); ++
i) {
1940 evaluation += linear_evaluator_.Violation(
i);
1941 DCHECK_GE(linear_evaluator_.Violation(
i), 0);
1945 for (
const auto& ct : constraints_) {
1946 evaluation += ct->violation();
1947 DCHECK_GE(ct->violation(), 0);
1953 DCHECK(cp_model_.has_objective());
1954 return linear_evaluator_.Activity(0);
1958 return linear_evaluator_.num_constraints();
1962 return static_cast<int>(constraints_.size());
1966 return linear_evaluator_.num_constraints() +
1967 static_cast<int>(constraints_.size());
1972 for (
int c = 0; c < linear_evaluator_.num_constraints(); ++c) {
1973 if (linear_evaluator_.Violation(c) > 0) {
1977 for (
const auto& constraint : constraints_) {
1978 if (constraint->violation() > 0) {
1986 if (c < linear_evaluator_.num_constraints()) {
1987 return linear_evaluator_.Violation(c);
1989 return constraints_[c - linear_evaluator_.num_constraints()]->violation();
1994 if (c < linear_evaluator_.num_constraints()) {
1995 return linear_evaluator_.IsViolated(c);
1997 return constraints_[c - linear_evaluator_.num_constraints()]->violation() >
2004 double result = linear_evaluator_.WeightedViolation(weights);
2006 const int num_linear_constraints = linear_evaluator_.num_constraints();
2007 for (
int c = 0; c < constraints_.size(); ++c) {
2008 result +=
static_cast<double>(constraints_[c]->violation()) *
2009 weights[num_linear_constraints + c];
2015 bool linear_only, absl::Span<const double> weights,
int var, int64_t delta,
2016 absl::Span<int64_t> mutable_solution)
const {
2017 double result = linear_evaluator_.WeightedViolationDelta(weights, var, delta);
2018 if (linear_only)
return result;
2021 const int64_t old_value = mutable_solution[var];
2022 mutable_solution[var] += delta;
2026 dtime_ += var_to_dtime_estimate_[var];
2028 const int num_linear_constraints = linear_evaluator_.num_constraints();
2029 const std::unique_ptr<CompiledConstraint>* data = constraints_.data();
2030 const auto non_linear_weights = weights.subspan(num_linear_constraints);
2031 for (
const int ct_index : var_to_constraints_[var]) {
2032 DCHECK_LT(ct_index, constraints_.size());
2033 const int64_t ct_delta =
2034 data[ct_index]->ViolationDelta(var, old_value, mutable_solution);
2035 result +=
static_cast<double>(ct_delta) * non_linear_weights[ct_index];
2039 mutable_solution[var] = old_value;
2045 return jump_value_optimal_[var];
2049 num_violated_constraint_per_var_ignoring_objective_.assign(
2050 cp_model_.variables_size(), 0);
2051 violated_constraints_.clear();
2052 const int num_constraints =
2054 for (
int c = 0; c < num_constraints; ++c) {
2061 auto [it, inserted] = violated_constraints_.insert(c);
2063 if (!inserted)
return;
2067 num_violated_constraint_per_var_ignoring_objective_[v] += 1;
2071 if (violated_constraints_.erase(c) == 1) {
2075 num_violated_constraint_per_var_ignoring_objective_[v] -= 1;
2080int64_t CompiledReservoirConstraint::BuildProfileAndReturnViolation(
2081 absl::Span<const int64_t>
solution) {
2083 capacity_value_ = ExprValue(capacity_,
solution);
2084 const int num_events = time_values_.size();
2086 for (
int i = 0;
i < num_events; ++
i) {
2087 time_values_[
i] = ExprValue(times_[
i],
solution);
2088 if (is_active_[
i] != std::nullopt &&
2089 LiteralValue(*is_active_[
i],
solution) == 0) {
2090 demand_values_[
i] = 0;
2092 demand_values_[
i] = ExprValue(demands_[
i],
solution);
2093 if (demand_values_[
i] != 0) {
2094 profile_.push_back({time_values_[
i], demand_values_[
i]});
2099 if (profile_.empty())
return 0;
2100 absl::c_sort(profile_);
2105 for (
int i = 1;
i < profile_.size(); ++
i) {
2106 if (profile_[
i].time == profile_[p].time) {
2107 profile_[p].demand += profile_[
i].demand;
2109 profile_[++p] = profile_[
i];
2112 profile_.resize(p + 1);
2115 int64_t overload = 0;
2116 int64_t current_load = 0;
2117 int64_t previous_time = std::numeric_limits<int64_t>::min();
2118 for (
int i = 0;
i < profile_.size(); ++
i) {
2120 const int64_t time = profile_[
i].time;
2121 if (current_load > capacity_value_) {
2122 overload =
CapAdd(overload,
CapProd(current_load - capacity_value_,
2123 time - previous_time));
2126 current_load += profile_[
i].demand;
2127 previous_time = time;
2132int64_t CompiledReservoirConstraint::IncrementalViolation(
2133 int var, absl::Span<const int64_t>
solution) {
2134 const int64_t capacity = ExprValue(capacity_,
solution);
2135 profile_delta_.clear();
2137 for (
const int i : dense_index_to_events_[var_to_dense_index_.at(var)]) {
2138 const int64_t time = ExprValue(times_[
i],
solution);
2140 if (is_active_[
i] == std::nullopt ||
2141 LiteralValue(*is_active_[
i],
solution) == 1) {
2142 demand = ExprValue(demands_[
i],
solution);
2145 if (time == time_values_[
i]) {
2146 if (demand != demand_values_[
i]) {
2148 profile_delta_.push_back({time, demand - demand_values_[
i]});
2152 if (demand_values_[
i] != 0) {
2153 profile_delta_.push_back({time_values_[
i], -demand_values_[
i]});
2157 profile_delta_.push_back({time, demand});
2165 if (capacity == capacity_value_ && profile_delta_.empty()) {
2168 absl::c_sort(profile_delta_);
2171 int64_t overload = 0;
2172 int64_t current_load = 0;
2173 int64_t previous_time = std::numeric_limits<int64_t>::min();
2181 const absl::Span<const Event> i_profile(profile_);
2182 const absl::Span<const Event> j_profile(profile_delta_);
2185 if (
i < i_profile.size() && j < j_profile.size()) {
2186 time = std::min(i_profile[
i].time, j_profile[j].time);
2187 }
else if (
i < i_profile.size()) {
2188 time = i_profile[
i].time;
2189 }
else if (j < j_profile.size()) {
2190 time = j_profile[j].time;
2198 if (current_load > capacity) {
2199 overload =
CapAdd(overload,
2200 CapProd(current_load - capacity, time - previous_time));
2204 while (
i < i_profile.size() && i_profile[
i].time == time) {
2205 current_load += i_profile[
i].demand;
2210 while (j < j_profile.size() && j_profile[j].time == time) {
2211 current_load += j_profile[j].demand;
2215 previous_time = time;
2220void CompiledReservoirConstraint::AppendVariablesForEvent(
2221 int i, std::vector<int>* result)
const {
2222 if (is_active_[
i] != std::nullopt) {
2225 for (
const int var : times_[
i].vars()) {
2228 for (
const int var : demands_[
i].vars()) {
2233void CompiledReservoirConstraint::InitializeDenseIndexToEvents() {
2236 CpModelProto unused;
2237 int num_dense_indices = 0;
2239 var_to_dense_index_[var] = num_dense_indices++;
2242 CompactVectorVector<int, int> event_to_dense_indices;
2243 event_to_dense_indices.reserve(times_.size());
2244 const int num_events = times_.size();
2245 std::vector<int> result;
2246 for (
int i = 0;
i < num_events; ++
i) {
2248 AppendVariablesForEvent(
i, &result);
2251 for (
int& var : result) {
2252 var = var_to_dense_index_.at(var);
2255 event_to_dense_indices.Add(result);
2260 dense_index_to_events_.ResetFromTranspose(event_to_dense_indices,
2266 std::vector<int> result;
2267 const int num_events = times_.size();
2268 for (
int i = 0;
i < num_events; ++
i) {
2269 AppendVariablesForEvent(
i, &result);
2271 for (
const int var : capacity_.vars()) {
2275 result.shrink_to_fit();
std::pair< iterator, bool > insert(T value)
std::vector< int64_t > FlattenedIntervals() const
bool Contains(int64_t value) const
int64_t Distance(int64_t value) const
static Domain AllValues()
static IntegralType CeilOfRatio(IntegralType numerator, IntegralType denominator)
static IntegralType FloorOfRatio(IntegralType numerator, IntegralType denominator)
void Set(IntegerType index)
::int32_t heads(int index) const
::int32_t literals(int index) const
::int32_t tails(int index) const
int64_t ComputeViolation(absl::Span< const int64_t > solution) override
CompiledAllDiffConstraint(const ConstraintProto &ct_proto)
--— CompiledAllDiffConstraint --—
CompiledBoolXorConstraint(const ConstraintProto &ct_proto)
--— CompiledBoolXorConstraint --—
int64_t ViolationDelta(int, int64_t, absl::Span< const int64_t > solution_with_new_value) override
Returns the delta if var changes from old_value to solution[var].
int64_t ComputeViolation(absl::Span< const int64_t > solution) override
void PerformMove(int var, int64_t old_value, absl::Span< const int64_t > new_solution) override
Updates the violation with the new value.
int64_t ComputeViolation(absl::Span< const int64_t > solution) override
~CompiledCircuitConstraint() override=default
int64_t ViolationDelta(int var, int64_t old_value, absl::Span< const int64_t > solution_with_new_value) override
Returns the delta if var changes from old_value to solution[var].
CompiledCircuitConstraint(const ConstraintProto &ct_proto)
CompiledConstraintWithProto(const ConstraintProto &ct_proto)
--— CompiledConstraintWithProto --—
std::vector< int > UsedVariables(const CpModelProto &model_proto) const final
This just returns the variables used by the stored ct_proto_.
const ConstraintProto & ct_proto() const
int64_t violation() const
The cached violation of this constraint.
virtual void PerformMove(int var, int64_t old_value, absl::Span< const int64_t > solution_with_new_value)
Updates the violation with the new value.
virtual int64_t ComputeViolation(absl::Span< const int64_t > solution)=0
void InitializeViolation(absl::Span< const int64_t > solution)
Recomputes the violation of the constraint from scratch.
virtual int64_t ViolationDelta(int var, int64_t old_value, absl::Span< const int64_t > solution_with_new_value)
Returns the delta if var changes from old_value to solution[var].
CompiledIntDivConstraint(const ConstraintProto &ct_proto)
--— CompiledIntDivConstraint --—
int64_t ComputeViolation(absl::Span< const int64_t > solution) override
int64_t ComputeViolation(absl::Span< const int64_t > solution) override
CompiledIntModConstraint(const ConstraintProto &ct_proto)
--— CompiledIntModConstraint --—
CompiledIntProdConstraint(const ConstraintProto &ct_proto)
--— CompiledIntProdConstraint --—
int64_t ComputeViolation(absl::Span< const int64_t > solution) override
int64_t ComputeViolation(absl::Span< const int64_t > solution) override
CompiledLinMaxConstraint(const ConstraintProto &ct_proto)
--— CompiledLinMaxConstraint --—
int64_t ComputeViolation(absl::Span< const int64_t > solution) override
CompiledNoOverlap2dConstraint(const ConstraintProto &ct_proto, const CpModelProto &cp_model)
int64_t ViolationDelta(int, int64_t, absl::Span< const int64_t > solution_with_new_value) final
std::vector< int > UsedVariables(const CpModelProto &model_proto) const final
int64_t ViolationDelta(int, int64_t, absl::Span< const int64_t > solution_with_new_value) final
--— CompiledNoOverlapWithTwoIntervals --—
std::vector< int > UsedVariables(const CpModelProto &model_proto) const final
std::vector< int > UsedVariables(const CpModelProto &model_proto) const final
bool has_routes() const
.operations_research.sat.RoutesConstraintProto routes = 23;
::int32_t enforcement_literal(int index) const
const ::operations_research::sat::IntervalConstraintProto & interval() const
const ::operations_research::sat::RoutesConstraintProto & routes() const
const ::operations_research::sat::CircuitConstraintProto & circuit() const
const ::operations_research::sat::NoOverlap2DConstraintProto & no_overlap_2d() const
const ::operations_research::sat::ConstraintProto & constraints(int index) const
const ::operations_research::sat::LinearExpressionProto & end() const
const ::operations_research::sat::LinearExpressionProto & start() const
::int64_t coeffs(int index) const
::int32_t vars(int index) const
void set_offset(::int64_t value)
int vars_size() const
repeated int32 vars = 1;
void UpdateScoreOnWeightUpdate(int c, absl::Span< const int64_t > jump_deltas, absl::Span< double > var_to_score_change)
Also for feasibility jump.
void AddOffset(int ct_index, int64_t offset)
double WeightedViolationDelta(absl::Span< const double > weights, int var, int64_t delta) const
bool IsViolated(int c) const
void ComputeInitialActivities(absl::Span< const int64_t > solution)
Compute activities.
void AddLiteral(int ct_index, int lit, int64_t coeff=1)
int NewConstraint(Domain domain)
Returns the index of the new constraint.
double WeightedViolation(absl::Span< const double > weights) const
bool ReduceBounds(int c, int64_t lb, int64_t ub)
bool VarIsConsistent(int var) const
Used to DCHECK the state of the evaluator.
void ClearAffectedVariables()
int64_t Activity(int c) const
Query violation.
void UpdateVariableAndScores(int var, int64_t delta, absl::Span< const double > weights, absl::Span< const int64_t > jump_deltas, absl::Span< double > jump_scores, std::vector< int > *constraints_with_changed_violations)
bool AppearsInViolatedConstraints(int var) const
void AddLinearExpression(int ct_index, const LinearExpressionProto &expr, int64_t multiplier)
void PrecomputeCompactView(absl::Span< const int64_t > var_max_variation)
int64_t Violation(int c) const
void AddEnforcementLiteral(int ct_index, int lit)
bool ViolationChangeIsConvex(int var) const
Checks if the jump value of a variable is always optimal.
std::vector< int64_t > SlopeBreakpoints(int var, int64_t current_value, const Domain &var_domain) const
void AddTerm(int ct_index, int var, int64_t coeff, int64_t offset=0)
double WeightedViolation(absl::Span< const double > weights) const
void ComputeAllNonLinearViolations(absl::Span< const int64_t > solution)
void UpdateLinearScores(int var, int64_t old_value, int64_t new_value, absl::Span< const double > weights, absl::Span< const int64_t > jump_deltas, absl::Span< double > jump_scores)
Function specific to the linear only feasibility jump.
bool VariableOnlyInLinearConstraintWithConvexViolationChange(int var) const
Indicates if the computed jump value is always the best choice.
void ComputeAllViolations(absl::Span< const int64_t > solution)
Recomputes the violations of all constraints (resp only non-linear one).
void UpdateNonLinearViolations(int var, int64_t old_value, absl::Span< const int64_t > new_solution)
Recomputes the violations of all impacted non linear constraints.
bool ReduceObjectiveBounds(int64_t lb, int64_t ub)
int64_t SumOfViolations()
Simple summation metric for the constraint and objective violations.
int64_t ObjectiveActivity() const
Returns the objective activity in the current state.
bool IsViolated(int c) const
int64_t Violation(int c) const
LsEvaluator(const CpModelProto &cp_model, const SatParameters ¶ms, TimeLimit *time_limit)
The cp_model must outlive this class.
void UpdateViolatedList()
absl::Span< const int > ConstraintToVars(int c) const
int NumNonLinearConstraints() const
int NumInfeasibleConstraints() const
double WeightedViolationDelta(bool linear_only, absl::Span< const double > weights, int var, int64_t delta, absl::Span< int64_t > mutable_solution) const
void RecomputeViolatedList(bool linear_only)
bool IsObjectiveConstraint(int c) const
int NumLinearConstraints() const
int NumEvaluatorConstraints() const
int x_intervals_size() const
repeated int32 x_intervals = 1;
::int32_t heads(int index) const
::int32_t literals(int index) const
::int32_t tails(int index) const
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void STLClearObject(T *obj)
void AddCircuitFlowConstraints(LinearIncrementalEvaluator &linear_evaluator, const ConstraintProto &ct_proto)
bool RefIsPositive(int ref)
int64_t OverlapOfTwoIntervals(const ConstraintProto &interval1, const ConstraintProto &interval2, absl::Span< const int64_t > solution)
--— CompiledNoOverlap2dConstraint --—
std::vector< int > UsedVariables(const ConstraintProto &ct)
std::vector< int > UsedIntervals(const ConstraintProto &ct)
Returns the sorted list of interval used by a constraint.
int64_t NoOverlapMinRepairDistance(const ConstraintProto &interval1, const ConstraintProto &interval2, absl::Span< const int64_t > solution)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Reads a Domain from the domain field of a proto.
int NegatedRef(int ref)
Small utility functions to deal with negative variable/literal references.
In SWIG mode, we don't want anything besides these top-level includes.
int64_t CapAdd(int64_t x, int64_t y)
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
int64_t CapSub(int64_t x, int64_t y)
ClosedInterval::Iterator end(ClosedInterval interval)
int64_t CapProd(int64_t x, int64_t y)
int64_t Max() const
Returns the max of the domain.
int64_t Min() const
Returns the min of the domain.