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/types/span.h"
33#include "ortools/sat/cp_model.pb.h"
45int64_t ExprValue(
const LinearExpressionProto& expr,
46 absl::Span<const int64_t>
solution) {
47 int64_t result = expr.offset();
48 for (
int i = 0;
i < expr.vars_size(); ++
i) {
49 result +=
solution[expr.vars(
i)] * expr.coeffs(
i);
54LinearExpressionProto ExprDiff(
const LinearExpressionProto&
a,
55 const LinearExpressionProto&
b) {
56 LinearExpressionProto result;
57 result.set_offset(
a.offset() -
b.offset());
58 result.mutable_vars()->Reserve(
a.vars().size() +
b.vars().size());
59 result.mutable_coeffs()->Reserve(
a.vars().size() +
b.vars().size());
60 for (
int i = 0;
i <
a.vars().
size(); ++
i) {
61 result.add_vars(
a.vars(
i));
62 result.add_coeffs(
a.coeffs(
i));
64 for (
int i = 0;
i <
b.vars().
size(); ++
i) {
65 result.add_vars(
b.vars(
i));
66 result.add_coeffs(-
b.coeffs(
i));
71LinearExpressionProto LinearExprSum(LinearExpressionProto
a,
72 LinearExpressionProto
b) {
73 LinearExpressionProto result;
74 result.set_offset(
a.offset() +
b.offset());
75 result.mutable_vars()->Reserve(
a.vars().size() +
b.vars().size());
76 result.mutable_coeffs()->Reserve(
a.vars().size() +
b.vars().size());
77 for (
const LinearExpressionProto& p : {
a,
b}) {
78 for (
int i = 0;
i < p.vars().
size(); ++
i) {
79 result.add_vars(p.vars(
i));
80 result.add_coeffs(p.coeffs(
i));
86LinearExpressionProto NegatedLinearExpression(LinearExpressionProto
a) {
87 LinearExpressionProto result =
a;
88 result.set_offset(-
a.offset());
89 for (int64_t& coeff : *result.mutable_coeffs()) {
95int64_t ExprMin(
const LinearExpressionProto& expr,
const CpModelProto&
model) {
96 int64_t result = expr.offset();
97 for (
int i = 0;
i < expr.vars_size(); ++
i) {
98 const IntegerVariableProto& var_proto =
model.variables(expr.vars(
i));
99 if (expr.coeffs(
i) > 0) {
100 result += expr.coeffs(
i) * var_proto.domain(0);
102 result += expr.coeffs(
i) * var_proto.domain(var_proto.domain_size() - 1);
108int64_t ExprMax(
const LinearExpressionProto& expr,
const CpModelProto&
model) {
109 int64_t result = expr.offset();
110 for (
int i = 0;
i < expr.vars_size(); ++
i) {
111 const IntegerVariableProto& var_proto =
model.variables(expr.vars(
i));
112 if (expr.coeffs(
i) > 0) {
113 result += expr.coeffs(
i) * var_proto.domain(var_proto.domain_size() - 1);
115 result += expr.coeffs(
i) * var_proto.domain(0);
121bool LiteralValue(
int lit, absl::Span<const int64_t>
solution) {
134 DCHECK(creation_phase_);
135 domains_.push_back(domain);
136 offsets_.push_back(0);
137 activities_.push_back(0);
138 num_false_enforcement_.push_back(0);
139 distances_.push_back(0);
140 is_violated_.push_back(
false);
141 return num_constraints_++;
145 DCHECK(creation_phase_);
147 if (literal_entries_.size() <=
var) {
148 literal_entries_.resize(
var + 1);
150 literal_entries_[
var].push_back(
156 DCHECK(creation_phase_);
166 DCHECK(creation_phase_);
168 if (coeff == 0)
return;
170 if (var_entries_.size() <=
var) {
171 var_entries_.resize(
var + 1);
173 if (!var_entries_[
var].empty() &&
175 var_entries_[
var].back().coefficient += coeff;
177 var_entries_[
var].pop_back();
180 var_entries_[
var].push_back({.ct_index =
ct_index, .coefficient = coeff});
187 DCHECK(creation_phase_);
192 int ct_index,
const LinearExpressionProto& expr, int64_t multiplier) {
193 DCHECK(creation_phase_);
195 for (
int i = 0;
i < expr.vars_size(); ++
i) {
196 if (expr.coeffs(
i) * multiplier == 0)
continue;
202 if (var_entries_.size() <=
var)
return true;
204 absl::flat_hash_set<int> visited;
205 for (
const Entry& entry : var_entries_[
var]) {
206 if (!visited.insert(entry.ct_index).second)
return false;
212 absl::Span<const int64_t>
solution) {
213 DCHECK(!creation_phase_);
216 activities_ = offsets_;
217 in_last_affected_variables_.resize(columns_.size(),
false);
218 num_false_enforcement_.assign(num_constraints_, 0);
221 const int num_vars = columns_.size();
222 for (
int var = 0;
var < num_vars; ++
var) {
223 const SpanData& data = columns_[
var];
226 if (
value == 0 && data.num_pos_literal > 0) {
227 const int* ct_indices = &ct_buffer_[data.start];
228 for (
int k = 0; k < data.num_pos_literal; ++k) {
229 num_false_enforcement_[ct_indices[k]]++;
233 if (
value == 1 && data.num_neg_literal > 0) {
234 const int* ct_indices = &ct_buffer_[data.start + data.num_pos_literal];
235 for (
int k = 0; k < data.num_neg_literal; ++k) {
236 num_false_enforcement_[ct_indices[k]]++;
240 if (
value != 0 && data.num_linear_entries > 0) {
241 const int* ct_indices =
242 &ct_buffer_[data.start + data.num_pos_literal + data.num_neg_literal];
243 const int64_t* coeffs = &coeff_buffer_[data.linear_start];
244 for (
int k = 0; k < data.num_linear_entries; ++k) {
245 activities_[ct_indices[k]] += coeffs[k] *
value;
251 for (
int c = 0; c < num_constraints_; ++c) {
252 distances_[c] = domains_[c].Distance(activities_[c]);
258 if (10 * last_affected_variables_.
size() < columns_.size()) {
260 in_last_affected_variables_.resize(columns_.size(),
false);
261 for (
const int var : last_affected_variables_) {
262 in_last_affected_variables_[
var] =
false;
266 in_last_affected_variables_.assign(columns_.size(),
false);
268 last_affected_variables_.
clear();
269 DCHECK(std::all_of(in_last_affected_variables_.begin(),
270 in_last_affected_variables_.end(),
271 [](
bool b) { return !b; }));
278 int c, absl::Span<const int64_t> jump_deltas,
279 absl::Span<double> var_to_score_change) {
280 if (c >= rows_.size())
return;
282 DCHECK_EQ(num_false_enforcement_[c], 0);
283 const SpanData& data = rows_[c];
288 const double enforcement_change =
static_cast<double>(-distances_[c]);
289 if (enforcement_change != 0.0) {
291 const int end = data.num_pos_literal + data.num_neg_literal;
293 for (
int k = 0; k <
end; ++k, ++
i) {
294 const int var = row_var_buffer_[
i];
295 if (!in_last_affected_variables_[
var]) {
296 var_to_score_change[
var] = enforcement_change;
297 in_last_affected_variables_[
var] =
true;
300 var_to_score_change[
var] += enforcement_change;
306 if (data.num_linear_entries > 0) {
307 const int* row_vars = &row_var_buffer_[data.start + data.num_pos_literal +
308 data.num_neg_literal];
309 const int64_t* row_coeffs = &row_coeff_buffer_[data.linear_start];
310 num_ops_ += 2 * data.num_linear_entries;
315 const Domain& rhs = domains_[c];
316 const int64_t rhs_min = rhs.
Min();
317 const int64_t rhs_max = rhs.
Max();
319 const auto violation = [&rhs, rhs_min, rhs_max, is_simple](int64_t v) {
322 }
else if (v <= rhs_min) {
325 return is_simple ? int64_t{0} : rhs.
Distance(v);
329 const int64_t old_distance = distances_[c];
330 const int64_t activity = activities_[c];
331 for (
int k = 0; k < data.num_linear_entries; ++k) {
332 const int var = row_vars[k];
333 const int64_t coeff = row_coeffs[k];
335 violation(activity + coeff * jump_deltas[
var]) - old_distance;
336 if (!in_last_affected_variables_[
var]) {
337 var_to_score_change[
var] =
static_cast<double>(diff);
338 in_last_affected_variables_[
var] =
true;
341 var_to_score_change[
var] +=
static_cast<double>(diff);
347void LinearIncrementalEvaluator::UpdateScoreOnNewlyEnforced(
348 int c,
double weight, absl::Span<const int64_t> jump_deltas,
349 absl::Span<double> jump_scores) {
350 const SpanData& data = rows_[c];
354 const double weight_time_violation =
355 weight *
static_cast<double>(distances_[c]);
356 if (weight_time_violation > 0.0) {
358 const int end = data.num_pos_literal + data.num_neg_literal;
360 for (
int k = 0; k <
end; ++k, ++
i) {
361 const int var = row_var_buffer_[
i];
362 jump_scores[
var] -= weight_time_violation;
363 if (!in_last_affected_variables_[
var]) {
364 in_last_affected_variables_[
var] =
true;
372 int i = data.start + data.num_pos_literal + data.num_neg_literal;
373 int j = data.linear_start;
374 num_ops_ += 2 * data.num_linear_entries;
375 const int64_t old_distance = distances_[
c];
376 for (
int k = 0; k < data.num_linear_entries; ++k, ++
i, ++j) {
377 const int var = row_var_buffer_[
i];
378 const int64_t coeff = row_coeff_buffer_[j];
379 const int64_t new_distance =
380 domains_[
c].Distance(activities_[c] + coeff * jump_deltas[
var]);
382 weight *
static_cast<double>(new_distance - old_distance);
383 if (!in_last_affected_variables_[
var]) {
384 in_last_affected_variables_[
var] =
true;
391void LinearIncrementalEvaluator::UpdateScoreOnNewlyUnenforced(
392 int c,
double weight, absl::Span<const int64_t> jump_deltas,
393 absl::Span<double> jump_scores) {
394 const SpanData& data = rows_[
c];
399 const double weight_time_violation =
400 weight *
static_cast<double>(distances_[
c]);
401 if (weight_time_violation > 0.0) {
403 const int end = data.num_pos_literal + data.num_neg_literal;
405 for (
int k = 0; k <
end; ++k, ++
i) {
406 const int var = row_var_buffer_[
i];
407 jump_scores[
var] += weight_time_violation;
413 int i = data.start + data.num_pos_literal + data.num_neg_literal;
414 int j = data.linear_start;
415 num_ops_ += 2 * data.num_linear_entries;
416 const int64_t old_distance = distances_[
c];
417 for (
int k = 0; k < data.num_linear_entries; ++k, ++
i, ++j) {
418 const int var = row_var_buffer_[
i];
419 const int64_t coeff = row_coeff_buffer_[j];
420 const int64_t new_distance =
421 domains_[
c].Distance(activities_[c] + coeff * jump_deltas[
var]);
423 weight *
static_cast<double>(new_distance - old_distance);
424 if (!in_last_affected_variables_[
var]) {
425 in_last_affected_variables_[
var] =
true;
434void LinearIncrementalEvaluator::UpdateScoreOfEnforcementIncrease(
435 int c,
double score_change, absl::Span<const int64_t> jump_deltas,
436 absl::Span<double> jump_scores) {
437 if (score_change == 0.0)
return;
439 const SpanData& data = rows_[
c];
441 num_ops_ += data.num_pos_literal;
442 for (
int k = 0; k < data.num_pos_literal; ++k, ++
i) {
443 const int var = row_var_buffer_[
i];
444 if (jump_deltas[
var] == 1) {
445 jump_scores[
var] += score_change;
446 if (score_change < 0.0 && !in_last_affected_variables_[
var]) {
447 in_last_affected_variables_[
var] =
true;
452 num_ops_ += data.num_neg_literal;
453 for (
int k = 0; k < data.num_neg_literal; ++k, ++
i) {
454 const int var = row_var_buffer_[
i];
455 if (jump_deltas[
var] == -1) {
456 jump_scores[
var] += score_change;
457 if (score_change < 0.0 && !in_last_affected_variables_[
var]) {
458 in_last_affected_variables_[
var] =
true;
465void LinearIncrementalEvaluator::UpdateScoreOnActivityChange(
466 int c,
double weight, int64_t activity_delta,
467 absl::Span<const int64_t> jump_deltas, absl::Span<double> jump_scores) {
468 if (activity_delta == 0)
return;
469 const SpanData& data = rows_[
c];
480 const int64_t old_activity = activities_[
c];
481 const int64_t new_activity = old_activity + activity_delta;
484 if (new_activity > old_activity) {
485 min_range = old_activity - row_max_variations_[
c];
486 max_range = new_activity + row_max_variations_[
c];
488 min_range = new_activity - row_max_variations_[
c];
489 max_range = old_activity + row_max_variations_[
c];
493 if (Domain(min_range, max_range).IsIncludedIn(domains_[c]))
return;
499 static_cast<double>(domains_[
c].Distance(new_activity) - distances_[c]);
502 const int end = data.num_pos_literal + data.num_neg_literal;
504 for (
int k = 0; k <
end; ++k, ++
i) {
505 const int var = row_var_buffer_[
i];
507 if (
delta < 0.0 && !in_last_affected_variables_[
var]) {
508 in_last_affected_variables_[
var] =
true;
517 if (min_range >= domains_[c].Max() || max_range <= domains_[c].Min())
return;
520 if (data.num_linear_entries > 0) {
521 const int* row_vars = &row_var_buffer_[data.start + data.num_pos_literal +
522 data.num_neg_literal];
523 const int64_t* row_coeffs = &row_coeff_buffer_[data.linear_start];
524 num_ops_ += 2 * data.num_linear_entries;
529 const Domain& rhs = domains_[
c];
530 const int64_t rhs_min = rhs.Min();
531 const int64_t rhs_max = rhs.Max();
532 const bool is_simple = rhs.NumIntervals() == 2;
533 const auto violation = [&rhs, rhs_min, rhs_max, is_simple](int64_t v) {
536 }
else if (v <= rhs_min) {
539 return is_simple ? int64_t{0} : rhs.Distance(v);
543 const int64_t old_a_minus_new_a =
544 distances_[
c] - domains_[
c].Distance(new_activity);
545 for (
int k = 0; k < data.num_linear_entries; ++k) {
546 const int var = row_vars[k];
547 const int64_t impact = row_coeffs[k] * jump_deltas[
var];
548 const int64_t old_b = violation(old_activity + impact);
549 const int64_t new_b = violation(new_activity + impact);
556 const int64_t diff = old_a_minus_new_a + new_b - old_b;
561 jump_scores[
var] +=
weight *
static_cast<double>(diff);
562 if (!in_last_affected_variables_[
var]) {
563 in_last_affected_variables_[
var] =
true;
572 int var, int64_t
delta, absl::Span<const double> weights,
573 absl::Span<const int64_t> jump_deltas, absl::Span<double> jump_scores,
574 std::vector<int>* constraints_with_changed_violation) {
575 DCHECK(!creation_phase_);
577 if (
var >= columns_.size())
return;
579 const SpanData& data = columns_[
var];
581 num_ops_ += data.num_pos_literal;
582 for (
int k = 0; k < data.num_pos_literal; ++k, ++
i) {
583 const int c = ct_buffer_[
i];
586 num_false_enforcement_[c]--;
587 DCHECK_GE(num_false_enforcement_[c], 0);
588 if (num_false_enforcement_[c] == 0) {
589 UpdateScoreOnNewlyEnforced(c, weights[c], jump_deltas, jump_scores);
590 }
else if (num_false_enforcement_[c] == 1) {
591 const double enforcement_change =
592 weights[c] *
static_cast<double>(distances_[c]);
593 UpdateScoreOfEnforcementIncrease(c, enforcement_change, jump_deltas,
597 num_false_enforcement_[c]++;
598 if (num_false_enforcement_[c] == 1) {
599 UpdateScoreOnNewlyUnenforced(c, weights[c], jump_deltas, jump_scores);
600 }
else if (num_false_enforcement_[c] == 2) {
601 const double enforcement_change =
602 weights[c] *
static_cast<double>(distances_[c]);
603 UpdateScoreOfEnforcementIncrease(c, -enforcement_change, jump_deltas,
608 is_violated_[c] = v1 > 0;
610 constraints_with_changed_violation->push_back(c);
613 num_ops_ += data.num_neg_literal;
614 for (
int k = 0; k < data.num_neg_literal; ++k, ++
i) {
615 const int c = ct_buffer_[
i];
618 num_false_enforcement_[c]--;
619 DCHECK_GE(num_false_enforcement_[c], 0);
620 if (num_false_enforcement_[c] == 0) {
621 UpdateScoreOnNewlyEnforced(c, weights[c], jump_deltas, jump_scores);
622 }
else if (num_false_enforcement_[c] == 1) {
623 const double enforcement_change =
624 weights[c] *
static_cast<double>(distances_[c]);
625 UpdateScoreOfEnforcementIncrease(c, enforcement_change, jump_deltas,
629 num_false_enforcement_[c]++;
630 if (num_false_enforcement_[c] == 1) {
631 UpdateScoreOnNewlyUnenforced(c, weights[c], jump_deltas, jump_scores);
632 }
else if (num_false_enforcement_[c] == 2) {
633 const double enforcement_change =
634 weights[c] *
static_cast<double>(distances_[c]);
635 UpdateScoreOfEnforcementIncrease(c, -enforcement_change, jump_deltas,
640 is_violated_[c] = v1 > 0;
642 constraints_with_changed_violation->push_back(c);
645 int j = data.linear_start;
646 num_ops_ += 2 * data.num_linear_entries;
647 for (
int k = 0; k < data.num_linear_entries; ++k, ++
i, ++j) {
648 const int c = ct_buffer_[
i];
650 const int64_t coeff = coeff_buffer_[j];
652 if (num_false_enforcement_[c] == 1) {
656 const int64_t new_distance =
657 domains_[c].Distance(activities_[c] + coeff *
delta);
658 if (new_distance != distances_[c]) {
659 UpdateScoreOfEnforcementIncrease(
660 c, -weights[c] *
static_cast<double>(distances_[c] - new_distance),
661 jump_deltas, jump_scores);
663 }
else if (num_false_enforcement_[c] == 0) {
664 UpdateScoreOnActivityChange(c, weights[c], coeff *
delta, jump_deltas,
668 activities_[c] += coeff *
delta;
669 distances_[c] = domains_[c].Distance(activities_[c]);
671 is_violated_[c] = v1 > 0;
673 constraints_with_changed_violation->push_back(c);
679 return activities_[c];
683 return num_false_enforcement_[c] > 0 ? 0 : distances_[c];
687 DCHECK_EQ(is_violated_[c],
Violation(c) > 0);
688 return is_violated_[c];
692 if (domains_[c].Min() >= lb && domains_[c].Max() <= ub)
return false;
693 domains_[c] = domains_[c].IntersectionWith(
Domain(lb, ub));
694 distances_[c] = domains_[c].Distance(activities_[c]);
699 absl::Span<const double> weights)
const {
701 DCHECK_GE(weights.size(), num_constraints_);
702 for (
int c = 0; c < num_constraints_; ++c) {
703 if (num_false_enforcement_[c] > 0)
continue;
704 result += weights[c] *
static_cast<double>(distances_[c]);
715 absl::Span<const double> weights,
int var, int64_t
delta)
const {
717 if (
var >= columns_.size())
return 0.0;
718 const SpanData& data = columns_[
var];
722 num_ops_ += data.num_pos_literal;
723 for (
int k = 0; k < data.num_pos_literal; ++k, ++
i) {
724 const int c = ct_buffer_[
i];
725 if (num_false_enforcement_[c] == 0) {
727 DCHECK_EQ(
delta, -1);
728 result -= weights[c] *
static_cast<double>(distances_[c]);
730 if (
delta == 1 && num_false_enforcement_[c] == 1) {
731 result += weights[c] *
static_cast<double>(distances_[c]);
736 num_ops_ += data.num_neg_literal;
737 for (
int k = 0; k < data.num_neg_literal; ++k, ++
i) {
738 const int c = ct_buffer_[
i];
739 if (num_false_enforcement_[c] == 0) {
742 result -= weights[c] *
static_cast<double>(distances_[c]);
744 if (
delta == -1 && num_false_enforcement_[c] == 1) {
745 result += weights[c] *
static_cast<double>(distances_[c]);
750 int j = data.linear_start;
751 num_ops_ += 2 * data.num_linear_entries;
752 for (
int k = 0; k < data.num_linear_entries; ++k, ++
i, ++j) {
753 const int c = ct_buffer_[
i];
754 if (num_false_enforcement_[c] > 0)
continue;
755 const int64_t coeff = coeff_buffer_[j];
756 const int64_t old_distance = distances_[c];
757 const int64_t new_distance =
758 domains_[c].Distance(activities_[c] + coeff *
delta);
759 result += weights[c] *
static_cast<double>(new_distance - old_distance);
766 if (
var >= columns_.size())
return false;
767 for (
const int c : VarToConstraints(
var)) {
774 int var, int64_t current_value,
const Domain& var_domain)
const {
776 if (var_domain.
Size() <= 2 ||
var >= columns_.size())
return result;
778 const SpanData& data = columns_[
var];
779 int i = data.start + data.num_pos_literal + data.num_neg_literal;
780 int j = data.linear_start;
781 for (
int k = 0; k < data.num_linear_entries; ++k, ++
i, ++j) {
782 const int c = ct_buffer_[
i];
783 if (num_false_enforcement_[c] > 0)
continue;
788 const int64_t coeff = coeff_buffer_[j];
789 const int64_t activity = activities_[c] - current_value * coeff;
791 const int64_t slack_min =
CapSub(domains_[c].Min(), activity);
792 const int64_t slack_max =
CapSub(domains_[c].Max(), activity);
793 if (slack_min != std::numeric_limits<int64_t>::min()) {
794 const int64_t ceil_bp =
CeilOfRatio(slack_min, coeff);
795 if (ceil_bp != result.back() && var_domain.
Contains(ceil_bp)) {
796 result.push_back(ceil_bp);
798 const int64_t floor_bp =
FloorOfRatio(slack_min, coeff);
799 if (floor_bp != result.back() && var_domain.
Contains(floor_bp)) {
800 result.push_back(floor_bp);
803 if (slack_min != slack_max &&
804 slack_max != std::numeric_limits<int64_t>::min()) {
805 const int64_t ceil_bp =
CeilOfRatio(slack_max, coeff);
806 if (ceil_bp != result.back() && var_domain.
Contains(ceil_bp)) {
807 result.push_back(ceil_bp);
809 const int64_t floor_bp =
FloorOfRatio(slack_max, coeff);
810 if (floor_bp != result.back() && var_domain.
Contains(floor_bp)) {
811 result.push_back(floor_bp);
821 absl::Span<const int64_t> var_max_variation) {
822 creation_phase_ =
false;
823 if (num_constraints_ == 0)
return;
828 int total_linear_size = 0;
829 tmp_row_sizes_.assign(num_constraints_, 0);
830 tmp_row_num_positive_literals_.assign(num_constraints_, 0);
831 tmp_row_num_negative_literals_.assign(num_constraints_, 0);
832 tmp_row_num_linear_entries_.assign(num_constraints_, 0);
833 for (
const auto&
column : literal_entries_) {
834 total_size +=
column.size();
835 for (
const auto [c, is_positive] :
column) {
838 tmp_row_num_positive_literals_[c]++;
840 tmp_row_num_negative_literals_[c]++;
845 row_max_variations_.assign(num_constraints_, 0);
846 for (
int var = 0;
var < var_entries_.size(); ++
var) {
847 const int64_t
range = var_max_variation[
var];
849 total_size +=
column.size();
850 total_linear_size +=
column.size();
851 for (
const auto [c, coeff] :
column) {
853 tmp_row_num_linear_entries_[c]++;
854 row_max_variations_[c] =
855 std::max(row_max_variations_[c],
range * std::abs(coeff));
860 ct_buffer_.reserve(total_size);
861 coeff_buffer_.reserve(total_linear_size);
862 columns_.resize(std::max(literal_entries_.size(), var_entries_.size()));
863 for (
int var = 0;
var < columns_.size(); ++
var) {
864 columns_[
var].start =
static_cast<int>(ct_buffer_.size());
865 columns_[
var].linear_start =
static_cast<int>(coeff_buffer_.size());
866 if (
var < literal_entries_.size()) {
867 for (
const auto [c, is_positive] : literal_entries_[
var]) {
869 columns_[
var].num_pos_literal++;
870 ct_buffer_.push_back(c);
873 for (
const auto [c, is_positive] : literal_entries_[
var]) {
875 columns_[
var].num_neg_literal++;
876 ct_buffer_.push_back(c);
880 if (
var < var_entries_.size()) {
881 for (
const auto [c, coeff] : var_entries_[
var]) {
882 columns_[
var].num_linear_entries++;
883 ct_buffer_.push_back(c);
884 coeff_buffer_.push_back(coeff);
900 int linear_offset = 0;
901 rows_.resize(num_constraints_);
902 for (
int c = 0; c < num_constraints_; ++c) {
903 rows_[c].num_pos_literal = tmp_row_num_positive_literals_[c];
904 rows_[c].num_neg_literal = tmp_row_num_negative_literals_[c];
905 rows_[c].num_linear_entries = tmp_row_num_linear_entries_[c];
907 rows_[c].start = offset;
908 offset += tmp_row_sizes_[c];
909 tmp_row_sizes_[c] = rows_[c].start;
911 rows_[c].linear_start = linear_offset;
912 linear_offset += tmp_row_num_linear_entries_[c];
913 tmp_row_num_linear_entries_[c] = rows_[c].linear_start;
915 DCHECK_EQ(offset, total_size);
916 DCHECK_EQ(linear_offset, total_linear_size);
919 row_var_buffer_.resize(total_size);
920 row_coeff_buffer_.resize(total_linear_size);
921 for (
int var = 0;
var < columns_.size(); ++
var) {
922 const SpanData& data = columns_[
var];
924 for (
int k = 0; k < data.num_pos_literal; ++
i, ++k) {
925 const int c = ct_buffer_[
i];
926 row_var_buffer_[tmp_row_sizes_[c]++] =
var;
929 for (
int var = 0;
var < columns_.size(); ++
var) {
930 const SpanData& data = columns_[
var];
931 int i = data.start + data.num_pos_literal;
932 for (
int k = 0; k < data.num_neg_literal; ++
i, ++k) {
933 const int c = ct_buffer_[
i];
934 row_var_buffer_[tmp_row_sizes_[c]++] =
var;
937 for (
int var = 0;
var < columns_.size(); ++
var) {
938 const SpanData& data = columns_[
var];
939 int i = data.start + data.num_pos_literal + data.num_neg_literal;
940 int j = data.linear_start;
941 for (
int k = 0; k < data.num_linear_entries; ++
i, ++j, ++k) {
942 const int c = ct_buffer_[
i];
943 row_var_buffer_[tmp_row_sizes_[c]++] =
var;
944 row_coeff_buffer_[tmp_row_num_linear_entries_[c]++] = coeff_buffer_[j];
948 cached_deltas_.assign(columns_.size(), 0);
949 cached_scores_.assign(columns_.size(), 0);
954 for (
const int c : VarToConstraints(
var)) {
955 if (domains_[c].NumIntervals() > 2)
return false;
963 absl::Span<const int64_t>
solution) {
968 int var, int64_t old_value,
969 absl::Span<const int64_t> solution_with_new_value) {
974 absl::Span<const int64_t>
solution) {
981 const ConstraintProto& ct_proto)
982 : ct_proto_(ct_proto) {}
985 const CpModelProto& model_proto)
const {
988 const ConstraintProto& interval_proto = model_proto.constraints(i_var);
990 result.push_back(
var);
994 result.shrink_to_fit();
1001 const ConstraintProto& ct_proto)
1005 absl::Span<const int64_t>
solution) {
1006 int64_t sum_of_literals = 0;
1007 for (
const int lit :
ct_proto().bool_xor().literals()) {
1010 return 1 - (sum_of_literals % 2);
1015 absl::Span<const int64_t> ) {
1022 const ConstraintProto& ct_proto)
1026 absl::Span<const int64_t>
solution) {
1027 const int64_t target_value =
1029 int64_t max_of_expressions = std::numeric_limits<int64_t>::min();
1030 for (
const LinearExpressionProto& expr :
ct_proto().lin_max().exprs()) {
1031 const int64_t expr_value = ExprValue(expr,
solution);
1032 max_of_expressions = std::max(max_of_expressions, expr_value);
1034 return std::max(target_value - max_of_expressions, int64_t{0});
1040 const ConstraintProto& ct_proto)
1044 absl::Span<const int64_t>
solution) {
1045 const int64_t target_value =
1047 int64_t prod_value = 1;
1048 for (
const LinearExpressionProto& expr :
ct_proto().int_prod().exprs()) {
1049 prod_value *= ExprValue(expr,
solution);
1051 return std::abs(target_value - prod_value);
1057 const ConstraintProto& ct_proto)
1061 absl::Span<const int64_t>
solution) {
1062 const int64_t target_value =
1064 DCHECK_EQ(
ct_proto().int_div().exprs_size(), 2);
1065 const int64_t div_value = ExprValue(
ct_proto().int_div().exprs(0),
solution) /
1067 return std::abs(target_value - div_value);
1073 const ConstraintProto& ct_proto)
1077 absl::Span<const int64_t>
solution) {
1078 const int64_t target_value =
1080 DCHECK_EQ(
ct_proto().int_mod().exprs_size(), 2);
1082 const int64_t expr_value = ExprValue(
ct_proto().int_mod().exprs(0),
solution);
1083 const int64_t mod_value = ExprValue(
ct_proto().int_mod().exprs(1),
solution);
1084 const int64_t rhs = expr_value % mod_value;
1085 if ((expr_value >= 0 && target_value >= 0) ||
1086 (expr_value <= 0 && target_value <= 0)) {
1088 return std::min({std::abs(target_value - rhs),
1089 std::abs(target_value) + std::abs(mod_value - rhs),
1090 std::abs(rhs) + std::abs(mod_value - target_value)});
1095 return std::abs(target_value) + std::abs(expr_value);
1102 const ConstraintProto& ct_proto)
1106 absl::Span<const int64_t>
solution) {
1108 for (
const LinearExpressionProto& expr :
ct_proto().all_diff().exprs()) {
1109 values_.push_back(ExprValue(expr,
solution));
1111 std::sort(values_.begin(), values_.end());
1113 int64_t
value = values_[0];
1116 for (
int i = 1;
i < values_.size(); ++
i) {
1117 const int64_t new_value = values_[
i];
1118 if (new_value ==
value) {
1121 violation += counter * (counter - 1) / 2;
1126 violation += counter * (counter - 1) / 2;
1133 int interval_0,
int interval_1,
const CpModelProto& cp_model) {
1134 const ConstraintProto& ct0 = cp_model.constraints(interval_0);
1135 const ConstraintProto& ct1 = cp_model.constraints(interval_1);
1139 ct0.enforcement_literal().size() + ct1.enforcement_literal().size();
1140 if (num_enforcements_ > 0) {
1141 enforcements_.reset(
new int[num_enforcements_]);
1143 for (
const int lit : ct0.enforcement_literal()) enforcements_[
i++] =
lit;
1144 for (
const int lit : ct1.enforcement_literal()) enforcements_[
i++] =
lit;
1149 end_minus_start_1_ =
1150 ExprDiff(LinearExprSum(ct0.interval().start(), ct0.interval().size()),
1151 ct1.interval().start());
1152 end_minus_start_2_ =
1153 ExprDiff(LinearExprSum(ct1.interval().start(), ct1.interval().size()),
1154 ct0.interval().start());
1158int64_t NoOverlapBetweenTwoIntervals::ComputeViolationInternal(
1159 absl::Span<const int64_t>
solution) {
1160 for (
int i = 0;
i < num_enforcements_; ++
i) {
1161 if (!LiteralValue(enforcements_[
i],
solution))
return 0;
1163 const int64_t diff1 = ExprValue(end_minus_start_1_,
solution);
1164 const int64_t diff2 = ExprValue(end_minus_start_2_,
solution);
1165 return std::max(std::min(diff1, diff2), int64_t{0});
1169 const CpModelProto& )
const {
1170 std::vector<int> result;
1171 for (
int i = 0;
i < num_enforcements_; ++
i) {
1174 for (
const int var : end_minus_start_1_.vars()) {
1177 for (
const int var : end_minus_start_2_.vars()) {
1181 result.shrink_to_fit();
1188 const ConstraintProto& interval2,
1189 absl::Span<const int64_t>
solution) {
1190 for (
const int lit : interval1.enforcement_literal()) {
1193 for (
const int lit : interval2.enforcement_literal()) {
1197 const int64_t start1 = ExprValue(interval1.interval().start(),
solution);
1198 const int64_t end1 = ExprValue(interval1.interval().end(),
solution);
1200 const int64_t start2 = ExprValue(interval2.interval().start(),
solution);
1201 const int64_t end2 = ExprValue(interval2.interval().end(),
solution);
1203 if (start1 >= end2 || start2 >= end1)
return 0;
1207 return std::max(std::min(std::min(end2 - start2, end1 - start1),
1208 std::min(end2 - start1, end1 - start2)),
1213 const ConstraintProto& interval2,
1214 absl::Span<const int64_t>
solution) {
1215 for (
const int lit : interval1.enforcement_literal()) {
1218 for (
const int lit : interval2.enforcement_literal()) {
1222 const int64_t start1 = ExprValue(interval1.interval().start(),
solution);
1223 const int64_t end1 = ExprValue(interval1.interval().end(),
solution);
1225 const int64_t start2 = ExprValue(interval2.interval().start(),
solution);
1226 const int64_t end2 = ExprValue(interval2.interval().end(),
solution);
1228 return std::max(std::min(end2 - start1, end1 - start2), int64_t{0});
1232 const ConstraintProto& ct_proto,
const CpModelProto& cp_model)
1236 absl::Span<const int64_t>
solution) {
1237 DCHECK_GE(
ct_proto().no_overlap_2d().x_intervals_size(), 2);
1238 const int size =
ct_proto().no_overlap_2d().x_intervals_size();
1241 for (
int i = 0;
i + 1 <
size; ++
i) {
1242 const ConstraintProto& x_i =
1243 cp_model_.constraints(
ct_proto().no_overlap_2d().x_intervals(
i));
1244 const ConstraintProto& y_i =
1245 cp_model_.constraints(
ct_proto().no_overlap_2d().y_intervals(
i));
1246 for (
int j =
i + 1; j <
size; ++j) {
1247 const ConstraintProto& x_j =
1248 cp_model_.constraints(
ct_proto().no_overlap_2d().x_intervals(j));
1249 const ConstraintProto& y_j =
1250 cp_model_.constraints(
ct_proto().no_overlap_2d().y_intervals(j));
1288 absl::Span<const int64_t> new_solution)
override;
1290 int var, int64_t old_value,
1291 absl::Span<const int64_t> solution_with_new_value)
override;
1295 void emplace_back(
const int*
start,
const int*
end);
1296 void reset(
int num_nodes);
1298 int num_components = 0;
1299 std::vector<bool> skipped;
1300 std::vector<int> root;
1302 void InitGraph(absl::Span<const int64_t>
solution);
1303 bool UpdateGraph(
int var, int64_t
value);
1304 int64_t ViolationForCurrentGraph();
1306 absl::flat_hash_map<int, std::vector<int>> arcs_by_lit_;
1307 absl::Span<const int> literals_;
1308 absl::Span<const int> tails_;
1309 absl::Span<const int> heads_;
1311 std::vector<DenseSet<int>> graph_;
1313 SccOutput committed_sccs_;
1314 std::vector<bool> has_in_arc_;
1319void CompiledCircuitConstraint::SccOutput::emplace_back(
int const*
start,
1321 const int root_node = *
start;
1327 root[*
start] = root_node;
1331void CompiledCircuitConstraint::SccOutput::reset(
int num_nodes) {
1334 root.resize(num_nodes);
1336 skipped.resize(num_nodes);
1342 const bool routes =
ct_proto.has_routes();
1344 heads_ = absl::MakeConstSpan(routes ?
ct_proto.routes().heads()
1346 literals_ = absl::MakeConstSpan(routes ?
ct_proto.routes().literals()
1348 graph_.resize(*absl::c_max_element(tails_) + 1);
1349 for (
int i = 0;
i < literals_.size(); ++
i) {
1350 arcs_by_lit_[literals_[
i]].push_back(
i);
1354void CompiledCircuitConstraint::InitGraph(absl::Span<const int64_t>
solution) {
1358 for (
int i = 0;
i < tails_.size(); ++
i) {
1359 if (!LiteralValue(literals_[
i],
solution))
continue;
1360 graph_[tails_[
i]].insert(heads_[
i]);
1364bool CompiledCircuitConstraint::UpdateGraph(
int var, int64_t
value) {
1365 bool needs_update =
false;
1366 const int enabled_lit =
1368 const int disabled_lit =
NegatedRef(enabled_lit);
1369 for (
const int arc : arcs_by_lit_[disabled_lit]) {
1373 needs_update = needs_update ||
tail !=
head;
1374 graph_[tails_[
arc]].erase(heads_[
arc]);
1376 for (
const int arc : arcs_by_lit_[enabled_lit]) {
1380 needs_update = needs_update ||
1381 committed_sccs_.root[
tail] != committed_sccs_.root[
head];
1382 graph_[tails_[
arc]].insert(heads_[
arc]);
1384 return needs_update;
1388 int var, int64_t, absl::Span<const int64_t> new_solution) {
1389 UpdateGraph(
var, new_solution[
var]);
1391 std::swap(committed_sccs_, sccs_);
1395 absl::Span<const int64_t>
solution) {
1397 int64_t result = ViolationForCurrentGraph();
1398 std::swap(committed_sccs_, sccs_);
1403 int var, int64_t old_value,
1404 absl::Span<const int64_t> solution_with_new_value) {
1406 if (UpdateGraph(
var, solution_with_new_value[
var])) {
1407 result = ViolationForCurrentGraph() -
violation_;
1409 UpdateGraph(
var, old_value);
1413int64_t CompiledCircuitConstraint::ViolationForCurrentGraph() {
1414 const int num_nodes = graph_.size();
1415 sccs_.reset(num_nodes);
1416 scc_finder_.FindStronglyConnectedComponents(num_nodes, graph_, &sccs_);
1419 if (sccs_.num_components == 0)
return 0;
1422 int num_half_connected_components = 0;
1423 has_in_arc_.clear();
1424 has_in_arc_.resize(num_nodes,
false);
1426 if (sccs_.skipped[
tail])
continue;
1427 for (
const int head : graph_[
tail]) {
1428 const int head_root = sccs_.root[
head];
1429 if (sccs_.root[
tail] == head_root)
continue;
1430 if (has_in_arc_[head_root])
continue;
1431 if (sccs_.skipped[head_root])
continue;
1432 has_in_arc_[head_root] =
true;
1433 ++num_half_connected_components;
1436 const int64_t
violation = sccs_.num_components - 1 + sccs_.num_components -
1437 num_half_connected_components - 1 +
1438 (
ct_proto().has_routes() ? sccs_.skipped[0] : 0);
1439 VLOG(2) <<
"#SCCs=" << sccs_.num_components <<
" #nodes=" << num_nodes
1440 <<
" #half_connected_components=" << num_half_connected_components
1446 const ConstraintProto& ct_proto) {
1447 const bool routes = ct_proto.has_routes();
1448 auto heads = routes ? ct_proto.routes().heads() : ct_proto.circuit().heads();
1449 auto tails = routes ? ct_proto.routes().tails() : ct_proto.circuit().tails();
1451 routes ? ct_proto.routes().literals() : ct_proto.circuit().literals();
1453 std::vector<std::vector<int>> inflow_lits;
1454 std::vector<std::vector<int>> outflow_lits;
1455 for (
int i = 0;
i < heads.size(); ++
i) {
1456 if (heads[
i] >= inflow_lits.size()) {
1457 inflow_lits.resize(heads[
i] + 1);
1459 inflow_lits[heads[
i]].push_back(literals[
i]);
1460 if (tails[
i] >= outflow_lits.size()) {
1461 outflow_lits.resize(tails[
i] + 1);
1463 outflow_lits[tails[
i]].push_back(literals[
i]);
1466 const int depot_net_flow = linear_evaluator.
NewConstraint({0, 0});
1467 for (
const int lit : inflow_lits[0]) {
1470 for (
const int lit : outflow_lits[0]) {
1474 for (
int i = routes ? 1 : 0;
i < inflow_lits.size(); ++
i) {
1475 const int inflow_ct = linear_evaluator.
NewConstraint({1, 1});
1476 for (
const int lit : inflow_lits[
i]) {
1480 for (
int i = routes ? 1 : 0;
i < outflow_lits.size(); ++
i) {
1481 const int outflow_ct = linear_evaluator.
NewConstraint({1, 1});
1482 for (
const int lit : outflow_lits[
i]) {
1491 const SatParameters& params)
1492 : cp_model_(cp_model), params_(params) {
1493 var_to_constraints_.resize(cp_model_.variables_size());
1494 jump_value_optimal_.resize(cp_model_.variables_size(),
true);
1495 num_violated_constraint_per_var_ignoring_objective_.assign(
1496 cp_model_.variables_size(), 0);
1498 std::vector<bool> ignored_constraints(cp_model_.constraints_size(),
false);
1499 std::vector<ConstraintProto> additional_constraints;
1500 CompileConstraintsAndObjective(ignored_constraints, additional_constraints);
1501 BuildVarConstraintGraph();
1506 const CpModelProto& cp_model,
const SatParameters& params,
1507 const std::vector<bool>& ignored_constraints,
1508 const std::vector<ConstraintProto>& additional_constraints)
1509 : cp_model_(cp_model), params_(params) {
1510 var_to_constraints_.resize(cp_model_.variables_size());
1511 jump_value_optimal_.resize(cp_model_.variables_size(),
true);
1512 num_violated_constraint_per_var_ignoring_objective_.assign(
1513 cp_model_.variables_size(), 0);
1514 CompileConstraintsAndObjective(ignored_constraints, additional_constraints);
1515 BuildVarConstraintGraph();
1519void LsEvaluator::BuildVarConstraintGraph() {
1521 for (std::vector<int>& ct_indices : var_to_constraints_) ct_indices.clear();
1522 constraint_to_vars_.resize(constraints_.size());
1527 constraints_[
ct_index]->UsedVariables(cp_model_);
1528 for (
const int var : constraint_to_vars_[
ct_index]) {
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;
1561void LsEvaluator::CompileOneConstraint(
const ConstraintProto&
ct) {
1562 switch (
ct.constraint_case()) {
1563 case ConstraintProto::ConstraintCase::kBoolOr: {
1566 for (
const int lit :
ct.enforcement_literal()) {
1569 for (
const int lit :
ct.bool_or().literals()) {
1574 case ConstraintProto::ConstraintCase::kBoolAnd: {
1575 const int num_literals =
ct.bool_and().literals_size();
1578 for (
const int lit :
ct.enforcement_literal()) {
1581 for (
const int lit :
ct.bool_and().literals()) {
1586 case ConstraintProto::ConstraintCase::kAtMostOne: {
1587 DCHECK(
ct.enforcement_literal().empty());
1589 for (
const int lit :
ct.at_most_one().literals()) {
1594 case ConstraintProto::ConstraintCase::kExactlyOne: {
1595 DCHECK(
ct.enforcement_literal().empty());
1597 for (
const int lit :
ct.exactly_one().literals()) {
1602 case ConstraintProto::ConstraintCase::kBoolXor: {
1603 constraints_.emplace_back(
new CompiledBoolXorConstraint(
ct));
1606 case ConstraintProto::ConstraintCase::kAllDiff: {
1607 constraints_.emplace_back(
new CompiledAllDiffConstraint(
ct));
1610 case ConstraintProto::ConstraintCase::kLinMax: {
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 =
1624 constraints_.emplace_back(
new CompiledLinMaxConstraint(
ct));
1627 case ConstraintProto::ConstraintCase::kIntProd: {
1628 constraints_.emplace_back(
new CompiledIntProdConstraint(
ct));
1631 case ConstraintProto::ConstraintCase::kIntDiv: {
1632 constraints_.emplace_back(
new CompiledIntDivConstraint(
ct));
1635 case ConstraintProto::ConstraintCase::kIntMod: {
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));
1641 case ConstraintProto::ConstraintCase::kLinear: {
1644 for (
const int lit :
ct.enforcement_literal()) {
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);
1654 case ConstraintProto::ConstraintCase::kNoOverlap: {
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 IntervalConstraintProto& interval_i =
1692 cp_model_.constraints(
ct.no_overlap().intervals(
i)).interval();
1693 const int64_t min_start_i = ExprMin(interval_i.start(), cp_model_);
1694 const int64_t max_end_i = ExprMax(interval_i.end(), cp_model_);
1695 for (
int j =
i + 1; j <
size; ++j) {
1696 const IntervalConstraintProto& interval_j =
1697 cp_model_.constraints(
ct.no_overlap().intervals(j)).interval();
1698 const int64_t min_start_j = ExprMin(interval_j.start(), cp_model_);
1699 const int64_t max_end_j = ExprMax(interval_j.end(), cp_model_);
1700 if (min_start_i >= max_end_j || min_start_j >= max_end_i)
continue;
1702 constraints_.emplace_back(
new NoOverlapBetweenTwoIntervals(
1703 ct.no_overlap().intervals(
i),
ct.no_overlap().intervals(j),
1710 case ConstraintProto::ConstraintCase::kCumulative: {
1711 LinearExpressionProto capacity =
ct.cumulative().capacity();
1712 std::vector<std::optional<int>> is_active;
1713 std::vector<LinearExpressionProto> times;
1714 std::vector<LinearExpressionProto> demands;
1715 const int num_intervals =
ct.cumulative().intervals().size();
1716 for (
int i = 0;
i < num_intervals; ++
i) {
1717 const ConstraintProto& interval_ct =
1718 cp_model_.constraints(
ct.cumulative().intervals(
i));
1719 if (interval_ct.enforcement_literal().empty()) {
1720 is_active.push_back(std::nullopt);
1721 is_active.push_back(std::nullopt);
1723 CHECK_EQ(interval_ct.enforcement_literal().size(), 1);
1724 is_active.push_back(interval_ct.enforcement_literal(0));
1725 is_active.push_back(interval_ct.enforcement_literal(0));
1729 times.push_back(interval_ct.interval().start());
1730 demands.push_back(
ct.cumulative().demands(
i));
1740 times.push_back(LinearExprSum(interval_ct.interval().start(),
1741 interval_ct.interval().size()));
1742 demands.push_back(NegatedLinearExpression(
ct.cumulative().demands(
i)));
1745 constraints_.emplace_back(
new CompiledReservoirConstraint(
1746 std::move(capacity), std::move(is_active), std::move(times),
1747 std::move(demands)));
1750 case ConstraintProto::ConstraintCase::kNoOverlap2D: {
1751 const auto& x_intervals =
ct.no_overlap_2d().x_intervals();
1752 const auto& y_intervals =
ct.no_overlap_2d().y_intervals();
1753 const int size = x_intervals.size();
1754 if (
size <= 1)
break;
1756 size > params_.feasibility_jump_max_expanded_constraint_size()) {
1757 CompiledNoOverlap2dConstraint* no_overlap_2d =
1758 new CompiledNoOverlap2dConstraint(
ct, cp_model_);
1759 constraints_.emplace_back(no_overlap_2d);
1763 for (
int i = 0;
i + 1 <
size; ++
i) {
1764 const IntervalConstraintProto& x_interval_i =
1765 cp_model_.constraints(x_intervals[
i]).interval();
1766 const int64_t x_min_start_i = ExprMin(x_interval_i.start(), cp_model_);
1767 const int64_t x_max_end_i = ExprMax(x_interval_i.end(), cp_model_);
1768 const IntervalConstraintProto& y_interval_i =
1769 cp_model_.constraints(y_intervals[
i]).interval();
1770 const int64_t y_min_start_i = ExprMin(y_interval_i.start(), cp_model_);
1771 const int64_t y_max_end_i = ExprMax(y_interval_i.end(), cp_model_);
1772 for (
int j =
i + 1; j <
size; ++j) {
1773 const IntervalConstraintProto& x_interval_j =
1774 cp_model_.constraints(x_intervals[j]).interval();
1775 const int64_t x_min_start_j =
1776 ExprMin(x_interval_j.start(), cp_model_);
1777 const int64_t x_max_end_j = ExprMax(x_interval_j.end(), cp_model_);
1778 const IntervalConstraintProto& y_interval_j =
1779 cp_model_.constraints(y_intervals[j]).interval();
1780 const int64_t y_min_start_j =
1781 ExprMin(y_interval_j.start(), cp_model_);
1782 const int64_t y_max_end_j = ExprMax(y_interval_j.end(), cp_model_);
1783 if (x_min_start_i >= x_max_end_j || x_min_start_j >= x_max_end_i ||
1784 y_min_start_i >= y_max_end_j || y_min_start_j >= y_max_end_i) {
1787 ConstraintProto* diffn = expanded_constraints_.add_constraints();
1788 diffn->mutable_no_overlap_2d()->add_x_intervals(x_intervals[
i]);
1789 diffn->mutable_no_overlap_2d()->add_x_intervals(x_intervals[j]);
1790 diffn->mutable_no_overlap_2d()->add_y_intervals(y_intervals[
i]);
1791 diffn->mutable_no_overlap_2d()->add_y_intervals(y_intervals[j]);
1792 CompiledNoOverlap2dConstraint* no_overlap_2d =
1793 new CompiledNoOverlap2dConstraint(*diffn, cp_model_);
1794 constraints_.emplace_back(no_overlap_2d);
1799 case ConstraintProto::ConstraintCase::kCircuit:
1800 case ConstraintProto::ConstraintCase::kRoutes:
1801 constraints_.emplace_back(
new CompiledCircuitConstraint(
ct));
1805 VLOG(1) <<
"Not implemented: " <<
ct.constraint_case();
1810void LsEvaluator::CompileConstraintsAndObjective(
1811 const std::vector<bool>& ignored_constraints,
1812 const std::vector<ConstraintProto>& additional_constraints) {
1813 constraints_.clear();
1816 if (cp_model_.has_objective()) {
1818 cp_model_.objective().domain().empty()
1822 for (
int i = 0;
i < cp_model_.objective().vars_size(); ++
i) {
1823 const int var = cp_model_.objective().vars(
i);
1824 const int64_t coeff = cp_model_.objective().coeffs(
i);
1829 for (
int c = 0;
c < cp_model_.constraints_size(); ++
c) {
1830 if (ignored_constraints[c])
continue;
1831 CompileOneConstraint(cp_model_.constraints(c));
1834 for (
const ConstraintProto&
ct : additional_constraints) {
1835 CompileOneConstraint(
ct);
1839 std::vector<int64_t> var_max_variations(cp_model_.variables().size());
1840 for (
int var = 0;
var < cp_model_.variables().
size(); ++
var) {
1841 const auto& domain = cp_model_.variables(
var).domain();
1842 var_max_variations[
var] = domain[domain.size() - 1] - domain[0];
1848 if (!cp_model_.has_objective())
return false;
1861 for (
const auto&
ct : constraints_) {
1869 absl::Span<const int64_t>
solution) {
1871 for (
const auto&
ct : constraints_) {
1877 int var, int64_t old_value, absl::Span<const int64_t> new_solution) {
1878 for (
const int general_ct_index : var_to_constraints_[
var]) {
1880 const int64_t v0 = constraints_[general_ct_index]->violation();
1881 constraints_[general_ct_index]->PerformMove(
var, old_value, new_solution);
1882 const int64_t violation_delta =
1883 constraints_[general_ct_index]->violation() - v0;
1884 if (violation_delta != 0) {
1885 last_update_violation_changes_.push_back(c);
1892 absl::Span<const double> weights,
1893 absl::Span<const int64_t> jump_deltas,
1894 absl::Span<double> jump_scores) {
1896 if (old_value == new_value)
return;
1897 last_update_violation_changes_.clear();
1900 jump_deltas, jump_scores,
1901 &last_update_violation_changes_);
1906 dtime_ += 1e-8 * last_update_violation_changes_.size();
1907 for (
const int c : last_update_violation_changes_) {
1913 int64_t evaluation = 0;
1917 evaluation += linear_evaluator_.
Violation(
i);
1918 DCHECK_GE(linear_evaluator_.
Violation(
i), 0);
1922 for (
const auto&
ct : constraints_) {
1923 evaluation +=
ct->violation();
1924 DCHECK_GE(
ct->violation(), 0);
1930 DCHECK(cp_model_.has_objective());
1931 return linear_evaluator_.
Activity(0);
1939 return static_cast<int>(constraints_.size());
1944 static_cast<int>(constraints_.size());
1950 if (linear_evaluator_.
Violation(c) > 0) {
1954 for (
const auto& constraint : constraints_) {
1955 if (constraint->violation() > 0) {
1966 return constraints_[c - linear_evaluator_.
num_constraints()]->violation();
1974 return constraints_[c - linear_evaluator_.
num_constraints()]->violation() >
1983 const int num_linear_constraints = linear_evaluator_.
num_constraints();
1984 for (
int c = 0; c < constraints_.size(); ++c) {
1985 result +=
static_cast<double>(constraints_[c]->violation()) *
1986 weights[num_linear_constraints + c];
1992 bool linear_only, absl::Span<const double> weights,
int var, int64_t
delta,
1993 absl::Span<int64_t> mutable_solution)
const {
1995 if (linear_only)
return result;
1998 const int64_t old_value = mutable_solution[
var];
2001 const int num_linear_constraints = linear_evaluator_.
num_constraints();
2002 for (
const int ct_index : var_to_constraints_[
var]) {
2005 dtime_ += 1e-8 *
static_cast<double>(constraint_to_vars_[
ct_index].size());
2007 DCHECK_LT(
ct_index, constraints_.size());
2008 const int64_t ct_delta = constraints_[
ct_index]->ViolationDelta(
2009 var, old_value, mutable_solution);
2010 result +=
static_cast<double>(ct_delta) *
2011 weights[
ct_index + num_linear_constraints];
2015 mutable_solution[
var] = old_value;
2021 return jump_value_optimal_[
var];
2025 num_violated_constraint_per_var_ignoring_objective_.assign(
2026 cp_model_.variables_size(), 0);
2027 violated_constraints_.clear();
2028 const int num_constraints =
2030 for (
int c = 0; c < num_constraints; ++c) {
2037 auto [it, inserted] = violated_constraints_.insert(c);
2039 if (!inserted)
return;
2043 num_violated_constraint_per_var_ignoring_objective_[v] += 1;
2047 if (violated_constraints_.erase(c) == 1) {
2051 num_violated_constraint_per_var_ignoring_objective_[v] -= 1;
2056int64_t CompiledReservoirConstraint::BuildProfileAndReturnViolation(
2057 absl::Span<const int64_t>
solution) {
2059 capacity_value_ = ExprValue(capacity_,
solution);
2060 const int num_events = time_values_.size();
2062 for (
int i = 0;
i < num_events; ++
i) {
2063 time_values_[
i] = ExprValue(times_[
i],
solution);
2064 if (is_active_[
i] != std::nullopt &&
2065 LiteralValue(*is_active_[
i],
solution) == 0) {
2066 demand_values_[
i] = 0;
2068 demand_values_[
i] = ExprValue(demands_[
i],
solution);
2069 if (demand_values_[
i] != 0) {
2070 profile_.push_back({time_values_[
i], demand_values_[
i]});
2075 if (profile_.empty())
return 0;
2076 absl::c_sort(profile_);
2081 for (
int i = 1;
i < profile_.size(); ++
i) {
2082 if (profile_[
i].
time == profile_[p].
time) {
2083 profile_[p].demand += profile_[
i].demand;
2085 profile_[++p] = profile_[
i];
2088 profile_.resize(p + 1);
2091 int64_t overload = 0;
2092 int64_t current_load = 0;
2093 int64_t previous_time = std::numeric_limits<int64_t>::min();
2094 for (
int i = 0;
i < profile_.size(); ++
i) {
2096 const int64_t
time = profile_[
i].time;
2097 if (current_load > capacity_value_) {
2098 overload =
CapAdd(overload,
CapProd(current_load - capacity_value_,
2099 time - previous_time));
2102 current_load += profile_[
i].demand;
2103 previous_time =
time;
2108int64_t CompiledReservoirConstraint::IncrementalViolation(
2110 const int64_t capacity = ExprValue(capacity_,
solution);
2111 profile_delta_.clear();
2113 for (
const int i : dense_index_to_events_[var_to_dense_index_.at(
var)]) {
2116 if (is_active_[
i] == std::nullopt ||
2117 LiteralValue(*is_active_[
i],
solution) == 1) {
2121 if (
time == time_values_[
i]) {
2122 if (
demand != demand_values_[
i]) {
2124 profile_delta_.push_back({
time,
demand - demand_values_[
i]});
2128 if (demand_values_[
i] != 0) {
2129 profile_delta_.push_back({time_values_[
i], -demand_values_[
i]});
2141 if (capacity == capacity_value_ && profile_delta_.empty()) {
2144 absl::c_sort(profile_delta_);
2147 int64_t overload = 0;
2148 int64_t current_load = 0;
2149 int64_t previous_time = std::numeric_limits<int64_t>::min();
2157 const absl::Span<const Event> i_profile(profile_);
2158 const absl::Span<const Event> j_profile(profile_delta_);
2161 if (
i < i_profile.size() && j < j_profile.size()) {
2163 }
else if (
i < i_profile.size()) {
2164 time = i_profile[
i].time;
2165 }
else if (j < j_profile.size()) {
2166 time = j_profile[j].time;
2174 if (current_load > capacity) {
2175 overload =
CapAdd(overload,
2176 CapProd(current_load - capacity,
time - previous_time));
2180 while (
i < i_profile.size() && i_profile[
i].time ==
time) {
2181 current_load += i_profile[
i].demand;
2186 while (j < j_profile.size() && j_profile[j].time ==
time) {
2187 current_load += j_profile[j].demand;
2191 previous_time =
time;
2196void CompiledReservoirConstraint::AppendVariablesForEvent(
2197 int i, std::vector<int>* result)
const {
2198 if (is_active_[
i] != std::nullopt) {
2201 for (
const int var : times_[
i].vars()) {
2204 for (
const int var : demands_[
i].vars()) {
2209void CompiledReservoirConstraint::InitializeDenseIndexToEvents() {
2212 CpModelProto unused;
2213 int num_dense_indices = 0;
2215 var_to_dense_index_[
var] = num_dense_indices++;
2218 CompactVectorVector<int, int> event_to_dense_indices;
2219 event_to_dense_indices.reserve(times_.size());
2220 const int num_events = times_.size();
2221 std::vector<int> result;
2222 for (
int i = 0;
i < num_events; ++
i) {
2224 AppendVariablesForEvent(
i, &result);
2227 for (
int&
var : result) {
2228 var = var_to_dense_index_.at(
var);
2231 event_to_dense_indices.Add(result);
2241 const CpModelProto& )
const {
2242 std::vector<int> result;
2243 const int num_events = times_.size();
2244 for (
int i = 0;
i < num_events; ++
i) {
2245 AppendVariablesForEvent(
i, &result);
2247 for (
const int var : capacity_.vars()) {
2251 result.shrink_to_fit();
std::vector< int64_t > FlattenedIntervals() const
bool Contains(int64_t value) const
int64_t Distance(int64_t value) const
static Domain AllValues()
void ResetFromTranspose(const CompactVectorVector< V, K > &other, int min_transpose_size=0)
Similar to ResetFromFlatMapping().
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
--— CompiledCircuitConstraint --—
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)
std::vector< int > UsedVariables(const CpModelProto &model_proto) const final
void ClearAndReserve(size_t size)
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.
int num_constraints() const
Model getters.
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
void UpdateViolatedList()
absl::Span< const int > ConstraintToVars(int c) const
int NumNonLinearConstraints() const
LsEvaluator(const CpModelProto &cp_model, const SatParameters ¶ms)
The cp_model must outlive this class.
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
std::vector< int > UsedVariables(const CpModelProto &model_proto) const final
NoOverlapBetweenTwoIntervals(int interval_0, int interval_1, const CpModelProto &cp_model)
--— NoOverlapBetweenTwoIntervals --—
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 --—
IntType CeilOfRatio(IntType numerator, IntType denominator)
IntType FloorOfRatio(IntType numerator, IntType denominator)
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)
int64_t CapSub(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
std::optional< int64_t > end
const std::optional< Range > & range