53 absl::btree_set<int>* deducible_vars,
54 absl::btree_set<int>* input_vars,
55 int* preferred_to_deduce) {
56 deducible_vars->clear();
58 *preferred_to_deduce = -1;
59 switch (ct.constraint_case()) {
60 case ConstraintProto::kLinear: {
64 if (!ct.enforcement_literal().empty()) {
67 for (
int i = 0;
i < ct.linear().vars_size(); ++
i) {
68 if (ct.linear().coeffs(
i) == 0)
continue;
69 deducible_vars->insert(ct.linear().vars(
i));
73 case ConstraintProto::kLinMax: {
75 for (
int i = 0;
i < ct.lin_max().target().vars_size(); ++
i) {
76 if (ct.lin_max().target().coeffs(
i) == 0)
continue;
77 deducible_vars->insert(ct.lin_max().target().vars(
i));
79 for (
const auto& expr : ct.lin_max().exprs()) {
80 for (
const int var : expr.vars()) {
81 input_vars->insert(var);
84 for (
const int var : *input_vars) {
85 deducible_vars->erase(var);
89 case ConstraintProto::kIntProd: {
90 absl::btree_map<int, int> variable_appearance_count;
91 for (
const int var : ct.int_prod().target().vars()) {
92 variable_appearance_count[var]++;
94 for (
const auto& expr : ct.int_prod().exprs()) {
95 for (
const int var : expr.vars()) {
96 variable_appearance_count[var]++;
99 for (
int i = 0;
i < ct.int_prod().target().vars_size(); ++
i) {
100 if (ct.int_prod().target().coeffs(
i) == 0)
continue;
101 const int var = ct.int_prod().target().vars(
i);
102 if (variable_appearance_count[var] == 1) {
103 deducible_vars->insert(var);
106 for (
const auto& expr : ct.int_prod().exprs()) {
107 for (
int i = 0;
i < expr.vars_size(); ++
i) {
108 const int var = expr.vars(
i);
109 if (variable_appearance_count[var] == 1) {
114 input_vars->insert(var);
118 for (
const auto& [var, count] : variable_appearance_count) {
120 input_vars->insert(var);
125 case ConstraintProto::kExactlyOne: {
126 for (
const int lit : ct.exactly_one().literals()) {
215 std::vector<int> num_times_variable_appears_as_input(model.variables_size());
216 std::vector<int> num_times_variable_appears_as_deducible(
217 model.variables_size());
218 std::vector<int> num_times_variable_appears_as_preferred_to_deduce(
219 model.variables_size());
221 struct ConstraintData {
224 absl::btree_set<int> deducible_vars;
225 absl::btree_set<int> input_vars;
227 int preferred_to_deduce;
233 bool is_linear_inequality;
236 std::vector<ConstraintData> constraint_data(model.constraints_size());
237 std::vector<std::vector<int>> constraints_per_var(model.variables_size());
240 model.variables_size());
241 Bitset64<int> var_in_objective_is_negative(model.variables_size());
247 for (
int i = 0;
i < model.objective().vars_size(); ++
i) {
248 if (model.objective().coeffs(
i) == 0)
continue;
249 var_appears_only_in_objective_and_linear.
Set(model.objective().vars(
i));
250 if (model.objective().coeffs(
i) < 0) {
251 var_in_objective_is_negative.
Set(model.objective().vars(
i));
255 for (
int c = 0; c < model.constraints_size(); ++c) {
256 ConstraintData& data = constraint_data[c];
257 const ConstraintProto& ct = model.constraints(c);
258 data.is_linear_inequality =
false;
260 &data.preferred_to_deduce);
267 if (ct.constraint_case() == ConstraintProto::kLinear &&
268 data.deducible_vars.empty() &&
270 ct.enforcement_literal().empty() && ct.linear().domain_size() == 2) {
272 data.is_linear_inequality =
true;
273 for (
int i = 0;
i < ct.linear().vars_size(); ++
i) {
274 const int var = ct.linear().vars(
i);
275 if (!var_appears_only_in_objective_and_linear.
IsSet(var)) {
276 data.input_vars.insert(var);
279 if (ct.linear().coeffs(
i) == 0)
continue;
280 if (std::abs(ct.linear().coeffs(
i)) == 1) {
281 data.deducible_vars.insert(var);
283 data.input_vars.insert(var);
289 var_appears_only_in_objective_and_linear.
Set(var,
false);
295 var_appears_only_in_objective_and_linear.
Set(var,
false);
304 std::vector<int> deducible_vars_to_remove;
305 for (
int c = 0; c < model.constraints_size(); ++c) {
306 deducible_vars_to_remove.clear();
307 ConstraintData& data = constraint_data[c];
308 if (!data.is_linear_inequality) {
311 for (
const int var : data.deducible_vars) {
312 if (!var_appears_only_in_objective_and_linear.
IsSet(var)) {
313 deducible_vars_to_remove.push_back(var);
314 data.input_vars.insert(var);
317 for (
const int var : deducible_vars_to_remove) {
318 data.deducible_vars.erase(var);
320 if (data.deducible_vars.empty()) {
321 data.is_linear_inequality =
false;
325 for (
int c = 0; c < constraint_data.size(); ++c) {
326 ConstraintData& data = constraint_data[c];
327 if (data.deducible_vars.empty()) {
328 data.input_vars.clear();
331 if (data.preferred_to_deduce != -1) {
332 num_times_variable_appears_as_preferred_to_deduce
333 [data.preferred_to_deduce]++;
335 for (
const int v : data.deducible_vars) {
336 constraints_per_var[v].push_back(c);
337 num_times_variable_appears_as_deducible[v]++;
339 for (
const int v : data.input_vars) {
340 constraints_per_var[v].push_back(c);
341 num_times_variable_appears_as_input[v]++;
347 for (
int v = 0; v < model.variables_size(); ++v) {
348 if (num_times_variable_appears_as_deducible[v] != 0) {
351 num_times_variable_appears_as_input[v] = 0;
352 var_is_primary.
Set(v);
353 for (
const int c : constraints_per_var[v]) {
354 ConstraintData& data = constraint_data[c];
355 data.deducible_vars.erase(v);
356 data.input_vars.erase(v);
358 constraints_per_var[v].clear();
367 std::vector<int> count_of_unresolved_linear_inequalities_per_var(
368 model.variables_size());
369 for (
int c = 0; c < model.constraints_size(); ++c) {
370 ConstraintData& data = constraint_data[c];
371 if (!data.is_linear_inequality) {
374 if (data.input_vars.size() + data.deducible_vars.size() > 1) {
375 for (
const int v : data.input_vars) {
376 count_of_unresolved_linear_inequalities_per_var[v]++;
378 for (
const int v : data.deducible_vars) {
379 count_of_unresolved_linear_inequalities_per_var[v]++;
393 std::deque<int> vars_queue;
394 for (
int v = 0; v < model.variables_size(); ++v) {
395 if (var_is_primary.
IsSet(v) || var_is_secondary.
IsSet(v)) {
398 vars_queue.push_back(v);
400 absl::c_stable_sort(vars_queue, [&](
int a,
int b) {
401 const int a_diff_usage = num_times_variable_appears_as_deducible[a] -
402 num_times_variable_appears_as_input[a];
403 const int b_diff_usage = num_times_variable_appears_as_deducible[
b] -
404 num_times_variable_appears_as_input[
b];
405 return std::make_tuple(
407 -num_times_variable_appears_as_preferred_to_deduce[a],
408 -num_times_variable_appears_as_deducible[a]) <
411 -num_times_variable_appears_as_preferred_to_deduce[
b],
412 -num_times_variable_appears_as_deducible[
b]);
414 std::vector<int> constraints_to_check;
415 while (!vars_queue.empty()) {
416 const int v = vars_queue.front();
417 vars_queue.pop_front();
418 if (var_is_secondary.
IsSet(v) || var_is_primary.
IsSet(v)) {
423 for (
const int c : constraints_per_var[v]) {
424 ConstraintData& data = constraint_data[c];
425 if (data.is_linear_inequality) {
428 if (data.deducible_vars.size() + data.input_vars.size() != 1) {
436 if (data.deducible_vars.empty()) {
440 data.input_vars.clear();
443 var_is_secondary.
Set(v);
452 if (!var_is_secondary.
IsSet(v)) {
453 if (var_appears_only_in_objective_and_linear.
IsSet(v) &&
454 count_of_unresolved_linear_inequalities_per_var[v] == 0) {
455 var_is_secondary.
Set(v);
459 model, v, constraints_per_var[v],
460 var_in_objective_is_negative.
IsSet(v),
466 var_is_primary.
Set(v);
472 auto update_constraints_after_var_is_decided = [&](
int v) {
473 for (
const int c : constraints_per_var[v]) {
474 ConstraintData& data = constraint_data[c];
475 data.deducible_vars.erase(v);
476 data.input_vars.erase(v);
477 if (data.input_vars.size() + data.deducible_vars.size() != 1) {
483 if (data.is_linear_inequality && data.deducible_vars.size() == 1) {
484 const int deducible_var = *data.deducible_vars.begin();
485 count_of_unresolved_linear_inequalities_per_var[deducible_var]--;
486 if (count_of_unresolved_linear_inequalities_per_var[deducible_var] ==
489 vars_queue.push_front(deducible_var);
492 if (data.deducible_vars.empty()) {
494 data.input_vars.clear();
497 constraints_to_check.push_back(c);
505 DCHECK(constraints_to_check.empty());
506 update_constraints_after_var_is_decided(v);
510 while (!constraints_to_check.empty()) {
511 const int c = constraints_to_check.back();
512 constraints_to_check.pop_back();
513 ConstraintData& data = constraint_data[c];
514 DCHECK_LE(data.deducible_vars.size(), 1);
515 if (data.deducible_vars.size() != 1) {
518 const int single_deducible_var = *data.deducible_vars.begin();
519 var_is_secondary.
Set(single_deducible_var);
520 update_constraints_after_var_is_decided(single_deducible_var);
532 &data.preferred_to_deduce);
533 for (
const int v : data.input_vars) {
534 if (var_is_secondary.
IsSet(v)) {
538 for (
const int v : data.deducible_vars) {
539 if (var_is_secondary.
IsSet(v) && v != var) {
550 Bitset64<int> dependent_variables_set(model.variables_size());
552 dependent_variables_set.
Set(var);
556 const ConstraintProto& ct =
558 switch (ct.constraint_case()) {
559 case ConstraintProto::kLinear: {
560 const LinearConstraintProto& linear = ct.linear();
563 int64_t coeff_of_var = 1;
564 for (
int j = 0; j < linear.vars_size(); ++j) {
565 if (linear.vars(j) == var) {
566 coeff_of_var = linear.coeffs(j);
569 DCHECK(!dependent_variables_set.
IsSet(linear.vars(j)));
570 sum_of_free_variables -=
571 linear.coeffs(j) * (*solution)[linear.vars(j)];
573 (*solution)[var] = sum_of_free_variables / coeff_of_var;
575 case ConstraintProto::kLinMax: {
576 int64_t max = std::numeric_limits<int64_t>::min();
577 for (
const auto& expr : ct.lin_max().exprs()) {
578 int64_t expr_value = expr.offset();
579 for (
int j = 0; j < expr.vars_size(); ++j) {
580 DCHECK(!dependent_variables_set.
IsSet(expr.vars(j)));
581 expr_value += expr.coeffs(j) * (*solution)[expr.vars(j)];
583 max = std::max(max, expr_value);
585 const LinearExpressionProto& target = ct.lin_max().target();
586 int64_t coeff_of_var = 1;
587 for (
int j = 0; j < target.vars_size(); ++j) {
588 if (target.vars(j) == var) {
589 coeff_of_var = target.coeffs(j);
592 DCHECK(!dependent_variables_set.
IsSet(target.vars(j)));
593 max -= target.coeffs(j) * (*solution)[target.vars(j)];
595 max -= target.offset();
596 (*solution)[var] = max / coeff_of_var;
599 case ConstraintProto::kIntProd: {
601 for (
const auto& expr : ct.int_prod().exprs()) {
602 int64_t expr_value = expr.offset();
603 for (
int j = 0; j < expr.vars_size(); ++j) {
604 DCHECK(!dependent_variables_set.
IsSet(expr.vars(j)));
605 expr_value += (*solution)[expr.vars(j)] * expr.coeffs(j);
607 product *= expr_value;
609 int64_t coeff_of_var = 1;
610 const LinearExpressionProto& target = ct.int_prod().target();
611 for (
int j = 0; j < target.vars_size(); ++j) {
612 if (target.vars(j) == var) {
613 coeff_of_var = target.coeffs(j);
616 DCHECK(!dependent_variables_set.
IsSet(target.vars(j)));
617 product -= target.coeffs(j) * (*solution)[target.vars(j)];
619 product -= target.offset();
620 (*solution)[var] = product / coeff_of_var;
622 case ConstraintProto::kExactlyOne: {
623 (*solution)[var] = 0;
625 for (
const int lit : ct.exactly_one().literals()) {
627 DCHECK(positive_ref == var ||
628 !dependent_variables_set.
IsSet(positive_ref));
630 : 1 - (*solution)[positive_ref];
633 (*solution)[var] ^= 1;
639 dependent_variables_set.
Set(var,
false);