OR-Tools  9.1
presolve_context.cc
Go to the documentation of this file.
1 // Copyright 2010-2021 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
15 
16 #include <algorithm>
17 #include <cstdint>
18 #include <limits>
19 #include <string>
20 
21 #include "ortools/base/map_util.h"
22 #include "ortools/base/mathutil.h"
25 
26 namespace operations_research {
27 namespace sat {
28 
30  return context->GetLiteralRepresentative(ref_);
31 }
32 
34  return context->GetVariableRepresentative(ref_);
35 }
36 
37 void PresolveContext::ClearStats() { stats_by_rule_name_.clear(); }
38 
39 int PresolveContext::NewIntVar(const Domain& domain) {
41  FillDomainInProto(domain, var);
43  return working_model->variables_size() - 1;
44 }
45 
47 
49  if (!gtl::ContainsKey(constant_to_ref_, cst)) {
50  constant_to_ref_[cst] = SavedVariable(working_model->variables_size());
51  IntegerVariableProto* const var_proto = working_model->add_variables();
52  var_proto->add_domain(cst);
53  var_proto->add_domain(cst);
55  }
56  return constant_to_ref_[cst].Get(this);
57 }
58 
59 // a => b.
62  ct->add_enforcement_literal(a);
63  ct->mutable_bool_and()->add_literals(b);
64 }
65 
66 // b => x in [lb, ub].
67 void PresolveContext::AddImplyInDomain(int b, int x, const Domain& domain) {
69 
70  // Doing it like this seems to use slightly less memory.
71  // TODO(user): Find the best way to create such small proto.
72  imply->mutable_enforcement_literal()->Resize(1, b);
73  LinearConstraintProto* mutable_linear = imply->mutable_linear();
74  mutable_linear->mutable_vars()->Resize(1, x);
75  mutable_linear->mutable_coeffs()->Resize(1, 1);
76  FillDomainInProto(domain, mutable_linear);
77 }
78 
79 bool PresolveContext::DomainIsEmpty(int ref) const {
80  return domains[PositiveRef(ref)].IsEmpty();
81 }
82 
83 bool PresolveContext::IsFixed(int ref) const {
84  DCHECK_LT(PositiveRef(ref), domains.size());
85  DCHECK(!DomainIsEmpty(ref));
86  return domains[PositiveRef(ref)].IsFixed();
87 }
88 
90  const int var = PositiveRef(ref);
91  return domains[var].Min() >= 0 && domains[var].Max() <= 1;
92 }
93 
94 bool PresolveContext::LiteralIsTrue(int lit) const {
96  if (RefIsPositive(lit)) {
97  return domains[lit].Min() == 1;
98  } else {
99  return domains[PositiveRef(lit)].Max() == 0;
100  }
101 }
102 
103 bool PresolveContext::LiteralIsFalse(int lit) const {
105  if (RefIsPositive(lit)) {
106  return domains[lit].Max() == 0;
107  } else {
108  return domains[PositiveRef(lit)].Min() == 1;
109  }
110 }
111 
112 int64_t PresolveContext::MinOf(int ref) const {
113  DCHECK(!DomainIsEmpty(ref));
114  return RefIsPositive(ref) ? domains[PositiveRef(ref)].Min()
115  : -domains[PositiveRef(ref)].Max();
116 }
117 
118 int64_t PresolveContext::MaxOf(int ref) const {
119  DCHECK(!DomainIsEmpty(ref));
120  return RefIsPositive(ref) ? domains[PositiveRef(ref)].Max()
121  : -domains[PositiveRef(ref)].Min();
122 }
123 
124 int64_t PresolveContext::MinOf(const LinearExpressionProto& expr) const {
125  int64_t result = expr.offset();
126  for (int i = 0; i < expr.vars_size(); ++i) {
127  const int64_t coeff = expr.coeffs(i);
128  if (coeff > 0) {
129  result += coeff * MinOf(expr.vars(i));
130  } else {
131  result += coeff * MaxOf(expr.vars(i));
132  }
133  }
134  return result;
135 }
136 
137 int64_t PresolveContext::MaxOf(const LinearExpressionProto& expr) const {
138  int64_t result = expr.offset();
139  for (int i = 0; i < expr.vars_size(); ++i) {
140  const int64_t coeff = expr.coeffs(i);
141  if (coeff > 0) {
142  result += coeff * MaxOf(expr.vars(i));
143  } else {
144  result += coeff * MinOf(expr.vars(i));
145  }
146  }
147  return result;
148 }
149 
151  const LinearExpressionProto& expr) const {
152  Domain result(expr.offset());
153  for (int i = 0; i < expr.vars_size(); ++i) {
154  result = result.AdditionWith(
155  DomainOf(expr.vars(i)).MultiplicationBy(expr.coeffs(i)));
156  }
157  return result;
158 }
159 
161  const LinearExpressionProto& expr) const {
162  if (expr.vars().size() != 1) return false;
163  return CanBeUsedAsLiteral(expr.vars(0));
164 }
165 
167  const LinearExpressionProto& expr) const {
168  const int ref = expr.vars(0);
169  return RefIsPositive(ref) == (expr.coeffs(0) > 0) ? ref : NegatedRef(ref);
170 }
171 
172 // Note that we only support converted intervals.
173 bool PresolveContext::IntervalIsConstant(int ct_ref) const {
175  if (!proto.enforcement_literal().empty()) return false;
176  if (!proto.interval().has_start_view()) return false;
177  for (const int var : proto.interval().start_view().vars()) {
178  if (!IsFixed(var)) return false;
179  }
180  for (const int var : proto.interval().size_view().vars()) {
181  if (!IsFixed(var)) return false;
182  }
183  for (const int var : proto.interval().end_view().vars()) {
184  if (!IsFixed(var)) return false;
185  }
186  return true;
187 }
188 
189 std::string PresolveContext::IntervalDebugString(int ct_ref) const {
190  if (IntervalIsConstant(ct_ref)) {
191  return absl::StrCat("interval_", ct_ref, "(", StartMin(ct_ref), "..",
192  EndMax(ct_ref), ")");
193  } else if (ConstraintIsOptional(ct_ref)) {
194  const int literal =
196  if (SizeMin(ct_ref) == SizeMax(ct_ref)) {
197  return absl::StrCat("interval_", ct_ref, "(lit=", literal, ", ",
198  StartMin(ct_ref), " --(", SizeMin(ct_ref), ")--> ",
199  EndMax(ct_ref), ")");
200  } else {
201  return absl::StrCat("interval_", ct_ref, "(lit=", literal, ", ",
202  StartMin(ct_ref), " --(", SizeMin(ct_ref), "..",
203  SizeMax(ct_ref), ")--> ", EndMax(ct_ref), ")");
204  }
205  } else if (SizeMin(ct_ref) == SizeMax(ct_ref)) {
206  return absl::StrCat("interval_", ct_ref, "(", StartMin(ct_ref), " --(",
207  SizeMin(ct_ref), ")--> ", EndMax(ct_ref), ")");
208  } else {
209  return absl::StrCat("interval_", ct_ref, "(", StartMin(ct_ref), " --(",
210  SizeMin(ct_ref), "..", SizeMax(ct_ref), ")--> ",
211  EndMax(ct_ref), ")");
212  }
213 }
214 
215 int64_t PresolveContext::StartMin(int ct_ref) const {
218  if (interval.has_start_view()) return MinOf(interval.start_view());
219  return MinOf(interval.start());
220 }
221 
222 int64_t PresolveContext::StartMax(int ct_ref) const {
225  if (interval.has_start_view()) return MaxOf(interval.start_view());
226  return MaxOf(interval.start());
227 }
228 
229 int64_t PresolveContext::EndMin(int ct_ref) const {
232  if (interval.has_end_view()) return MinOf(interval.end_view());
233  return MinOf(interval.end());
234 }
235 
236 int64_t PresolveContext::EndMax(int ct_ref) const {
239  if (interval.has_end_view()) return MaxOf(interval.end_view());
240  return MaxOf(interval.end());
241 }
242 
243 int64_t PresolveContext::SizeMin(int ct_ref) const {
246  if (interval.has_size_view()) return MinOf(interval.size_view());
247  return MinOf(interval.size());
248 }
249 
250 int64_t PresolveContext::SizeMax(int ct_ref) const {
253  if (interval.has_size_view()) return MaxOf(interval.size_view());
254  return MaxOf(interval.size());
255 }
256 
257 // Important: To be sure a variable can be removed, we need it to not be a
258 // representative of both affine and equivalence relation.
259 bool PresolveContext::VariableIsNotRepresentativeOfEquivalenceClass(
260  int var) const {
262  if (affine_relations_.ClassSize(var) > 1 &&
263  affine_relations_.Get(var).representative == var) {
264  return false;
265  }
266  if (var_equiv_relations_.ClassSize(var) > 1 &&
267  var_equiv_relations_.Get(var).representative == var) {
268  return false;
269  }
270  return true;
271 }
272 
274  const int var = PositiveRef(ref);
275  return VariableIsNotRepresentativeOfEquivalenceClass(var) &&
277 }
278 
279 // Tricky: If this variable is equivalent to another one (but not the
280 // representative) and appear in just one constraint, then this constraint must
281 // be the affine defining one. And in this case the code using this function
282 // should do the proper stuff.
284  if (!ConstraintVariableGraphIsUpToDate()) return false;
285  const int var = PositiveRef(ref);
286  return var_to_constraints_[var].size() == 1 && VariableIsRemovable(var);
287 }
288 
289 // Tricky: Same remark as for VariableIsUniqueAndRemovable().
291  if (!ConstraintVariableGraphIsUpToDate()) return false;
292  const int var = PositiveRef(ref);
293  return VariableIsRemovable(var) &&
294  var_to_constraints_[var].contains(kObjectiveConstraint) &&
295  var_to_constraints_[var].size() == 2;
296 }
297 
298 // Here, even if the variable is equivalent to others, if its affine defining
299 // constraints where removed, then it is not needed anymore.
301  if (!ConstraintVariableGraphIsUpToDate()) return false;
302  return var_to_constraints_[PositiveRef(ref)].empty();
303 }
304 
306  removed_variables_.insert(PositiveRef(ref));
307 }
308 
309 // Note(user): I added an indirection and a function for this to be able to
310 // display debug information when this return false. This should actually never
311 // return false in the cases where it is used.
313  // It is okay to reuse removed fixed variable.
314  if (IsFixed(ref)) return false;
315  if (!removed_variables_.contains(PositiveRef(ref))) return false;
316  if (!var_to_constraints_[PositiveRef(ref)].empty()) {
317  SOLVER_LOG(logger_, "Variable ", PositiveRef(ref),
318  " was removed, yet it appears in some constraints!");
319  SOLVER_LOG(logger_, "affine relation: ",
321  for (const int c : var_to_constraints_[PositiveRef(ref)]) {
322  SOLVER_LOG(
323  logger_, "constraint #", c, " : ",
324  c >= 0 ? working_model->constraints(c).ShortDebugString() : "");
325  }
326  }
327  return true;
328 }
329 
331  int ref) const {
332  if (!ConstraintVariableGraphIsUpToDate()) return false;
333  const int var = PositiveRef(ref);
334  return var_to_num_linear1_[var] == var_to_constraints_[var].size() ||
335  (var_to_constraints_[var].contains(kObjectiveConstraint) &&
336  var_to_num_linear1_[var] + 1 == var_to_constraints_[var].size());
337 }
338 
340  Domain result;
341  if (RefIsPositive(ref)) {
342  result = domains[ref];
343  } else {
344  result = domains[PositiveRef(ref)].Negation();
345  }
346  return result;
347 }
348 
349 bool PresolveContext::DomainContains(int ref, int64_t value) const {
350  if (!RefIsPositive(ref)) {
351  return domains[PositiveRef(ref)].Contains(-value);
352  }
353  return domains[ref].Contains(value);
354 }
355 
356 ABSL_MUST_USE_RESULT bool PresolveContext::IntersectDomainWith(
357  int ref, const Domain& domain, bool* domain_modified) {
358  DCHECK(!DomainIsEmpty(ref));
359  const int var = PositiveRef(ref);
360 
361  if (RefIsPositive(ref)) {
362  if (domains[var].IsIncludedIn(domain)) {
363  return true;
364  }
365  domains[var] = domains[var].IntersectionWith(domain);
366  } else {
367  const Domain temp = domain.Negation();
368  if (domains[var].IsIncludedIn(temp)) {
369  return true;
370  }
371  domains[var] = domains[var].IntersectionWith(temp);
372  }
373 
374  if (domain_modified != nullptr) {
375  *domain_modified = true;
376  }
378  if (domains[var].IsEmpty()) {
379  is_unsat_ = true;
380  return false;
381  }
382 
383  // Propagate the domain of the representative right away.
384  // Note that the recursive call should only by one level deep.
386  if (r.representative == var) return true;
388  DomainOf(var)
389  .AdditionWith(Domain(-r.offset))
391 }
392 
393 ABSL_MUST_USE_RESULT bool PresolveContext::IntersectDomainWith(
394  const LinearExpressionProto& expr, const Domain& domain,
395  bool* domain_modified) {
396  if (expr.vars().empty()) {
397  return domain.Contains(expr.offset());
398  }
399  if (expr.vars().size() == 1) { // Affine
400  return IntersectDomainWith(expr.vars(0),
401  domain.AdditionWith(Domain(-expr.offset()))
403  domain_modified);
404  }
405 
406  // We don't do anything for longer expression for now.
407  return true;
408 }
409 
410 ABSL_MUST_USE_RESULT bool PresolveContext::SetLiteralToFalse(int lit) {
411  const int var = PositiveRef(lit);
412  const int64_t value = RefIsPositive(lit) ? 0 : 1;
414 }
415 
416 ABSL_MUST_USE_RESULT bool PresolveContext::SetLiteralToTrue(int lit) {
417  return SetLiteralToFalse(NegatedRef(lit));
418 }
419 
422  if (ct.constraint_case() ==
423  ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET) {
424  return true;
425  }
426  for (const int literal : ct.enforcement_literal()) {
427  if (LiteralIsFalse(literal)) return true;
428  }
429  return false;
430 }
431 
433  const ConstraintProto& ct = working_model->constraints(ct_ref);
434  bool contains_one_free_literal = false;
435  for (const int literal : ct.enforcement_literal()) {
436  if (LiteralIsFalse(literal)) return false;
437  if (!LiteralIsTrue(literal)) contains_one_free_literal = true;
438  }
439  return contains_one_free_literal;
440 }
441 
442 void PresolveContext::UpdateRuleStats(const std::string& name, int num_times) {
443  // We only count if we are going to display it.
444  if (logger_->LoggingIsEnabled()) {
445  VLOG(2) << num_presolve_operations << " : " << name;
446  stats_by_rule_name_[name] += num_times;
447  }
448  num_presolve_operations += num_times;
449 }
450 
451 void PresolveContext::UpdateLinear1Usage(const ConstraintProto& ct, int c) {
452  const int old_var = constraint_to_linear1_var_[c];
453  if (old_var >= 0) {
454  var_to_num_linear1_[old_var]--;
455  }
456  if (ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
457  ct.linear().vars().size() == 1) {
458  const int var = PositiveRef(ct.linear().vars(0));
459  constraint_to_linear1_var_[c] = var;
460  var_to_num_linear1_[var]++;
461  }
462 }
463 
464 void PresolveContext::AddVariableUsage(int c) {
465  const ConstraintProto& ct = working_model->constraints(c);
466  constraint_to_vars_[c] = UsedVariables(ct);
467  constraint_to_intervals_[c] = UsedIntervals(ct);
468  for (const int v : constraint_to_vars_[c]) {
470  var_to_constraints_[v].insert(c);
471  }
472  for (const int i : constraint_to_intervals_[c]) interval_usage_[i]++;
473  UpdateLinear1Usage(ct, c);
474 }
475 
477  if (is_unsat_) return;
478  DCHECK_EQ(constraint_to_vars_.size(), working_model->constraints_size());
480 
481  // We don't optimize the interval usage as this is not super frequent.
482  for (const int i : constraint_to_intervals_[c]) interval_usage_[i]--;
483  constraint_to_intervals_[c] = UsedIntervals(ct);
484  for (const int i : constraint_to_intervals_[c]) interval_usage_[i]++;
485 
486  // For the variables, we avoid an erase() followed by an insert() for the
487  // variables that didn't change.
488  tmp_new_usage_ = UsedVariables(ct);
489  const std::vector<int>& old_usage = constraint_to_vars_[c];
490  const int old_size = old_usage.size();
491  int i = 0;
492  for (const int var : tmp_new_usage_) {
494  while (i < old_size && old_usage[i] < var) {
495  var_to_constraints_[old_usage[i]].erase(c);
496  ++i;
497  }
498  if (i < old_size && old_usage[i] == var) {
499  ++i;
500  } else {
501  var_to_constraints_[var].insert(c);
502  }
503  }
504  for (; i < old_size; ++i) var_to_constraints_[old_usage[i]].erase(c);
505  constraint_to_vars_[c] = tmp_new_usage_;
506 
507  UpdateLinear1Usage(ct, c);
508 }
509 
511  return constraint_to_vars_.size() == working_model->constraints_size();
512 }
513 
515  if (is_unsat_) return;
516  const int old_size = constraint_to_vars_.size();
517  const int new_size = working_model->constraints_size();
518  CHECK_LE(old_size, new_size);
519  constraint_to_vars_.resize(new_size);
520  constraint_to_linear1_var_.resize(new_size, -1);
521  constraint_to_intervals_.resize(new_size);
522  interval_usage_.resize(new_size);
523  for (int c = old_size; c < new_size; ++c) {
524  AddVariableUsage(c);
525  }
526 }
527 
528 // TODO(user): Also test var_to_constraints_ !!
530  if (is_unsat_) return true; // We do not care in this case.
531  if (constraint_to_vars_.size() != working_model->constraints_size()) {
532  LOG(INFO) << "Wrong constraint_to_vars size!";
533  return false;
534  }
535  for (int c = 0; c < constraint_to_vars_.size(); ++c) {
536  if (constraint_to_vars_[c] !=
538  LOG(INFO) << "Wrong variables usage for constraint: \n"
540  << "old_size: " << constraint_to_vars_[c].size();
541  return false;
542  }
543  }
544  int num_in_objective = 0;
545  for (int v = 0; v < var_to_constraints_.size(); ++v) {
546  if (var_to_constraints_[v].contains(kObjectiveConstraint)) {
547  ++num_in_objective;
548  if (!objective_map_.contains(v)) {
549  LOG(INFO) << "Variable " << v
550  << " is marked as part of the objective but isn't.";
551  return false;
552  }
553  }
554  }
555  if (num_in_objective != objective_map_.size()) {
556  LOG(INFO) << "Not all variables are marked as part of the objective";
557  return false;
558  }
559 
560  return true;
561 }
562 
563 // If a Boolean variable (one with domain [0, 1]) appear in this affine
564 // equivalence class, then we want its representative to be Boolean. Note that
565 // this is always possible because a Boolean variable can never be equal to a
566 // multiple of another if std::abs(coeff) is greater than 1 and if it is not
567 // fixed to zero. This is important because it allows to simply use the same
568 // representative for any referenced literals.
569 //
570 // Note(user): When both domain contains [0,1] and later the wrong variable
571 // become usable as boolean, then we have a bug. Because of that, the code
572 // for GetLiteralRepresentative() is not as simple as it should be.
573 bool PresolveContext::AddRelation(int x, int y, int64_t c, int64_t o,
574  AffineRelation* repo) {
575  // When the coefficient is larger than one, then if later one variable becomes
576  // Boolean, it must be the representative.
577  if (std::abs(c) != 1) return repo->TryAdd(x, y, c, o);
578 
581 
582  // To avoid integer overflow, we always want to use the representative with
583  // the smallest domain magnitude. Otherwise we might express a variable in say
584  // [0, 3] as ([x, x + 3] - x) for an arbitrary large x, and substituting
585  // something like this in a linear expression could break our overflow
586  // precondition.
587  //
588  // Note that if either rep_x or rep_y can be used as a literal, then it will
589  // also be the variable with the smallest domain magnitude (1 or 0 if fixed).
590  const int rep_x = repo->Get(x).representative;
591  const int rep_y = repo->Get(y).representative;
592  const int64_t m_x = std::max(std::abs(MinOf(rep_x)), std::abs(MaxOf(rep_x)));
593  const int64_t m_y = std::max(std::abs(MinOf(rep_y)), std::abs(MaxOf(rep_y)));
594  bool allow_rep_x = m_x < m_y;
595  bool allow_rep_y = m_y < m_x;
596  if (m_x == m_y) {
597  // If both magnitude are the same, we prefer a positive domain.
598  // This is important so we don't use [-1, 0] as a representative for [0, 1].
599  allow_rep_x = MinOf(rep_x) >= MinOf(rep_y);
600  allow_rep_y = MinOf(rep_y) >= MinOf(rep_x);
601  }
602  if (allow_rep_x && allow_rep_y) {
603  // If both representative are okay, we force the choice to the variable
604  // with lower index. This is needed because we have two "equivalence"
605  // relations, and we want the same representative in both.
606  if (rep_x < rep_y) {
607  allow_rep_y = false;
608  } else {
609  allow_rep_x = false;
610  }
611  }
612  return repo->TryAdd(x, y, c, o, allow_rep_x, allow_rep_y);
613 }
614 
615 // Note that we just add the relation to the var_equiv_relations_, not to the
616 // affine one. This is enough, and should prevent overflow in the affine
617 // relation class: if we keep chaining variable fixed to zero, the coefficient
618 // in the relation can overflow. For instance if x = 200 y and z = 200 t,
619 // nothing prevent us if all end up being zero, to say y = z, which will result
620 // in x = 200^2 t. If we make a few bad choices like this, then we can have an
621 // overflow.
624  DCHECK(IsFixed(var));
625  const int64_t min = MinOf(var);
626  if (gtl::ContainsKey(constant_to_ref_, min)) {
627  const int rep = constant_to_ref_[min].Get(this);
628  if (RefIsPositive(rep)) {
629  if (rep != var) {
630  AddRelation(var, rep, 1, 0, &var_equiv_relations_);
631  }
632  } else {
633  if (PositiveRef(rep) == var) {
634  CHECK_EQ(min, 0);
635  } else {
636  AddRelation(var, PositiveRef(rep), -1, 0, &var_equiv_relations_);
637  }
638  }
639  } else {
640  constant_to_ref_[min] = SavedVariable(var);
641  }
642 }
643 
645  const int var = PositiveRef(ref);
647  if (r.representative == var) return true;
648 
649  // Propagate domains both ways.
650  // var = coeff * rep + offset
652  DomainOf(var)
653  .AdditionWith(Domain(-r.offset))
655  return false;
656  }
659  .AdditionWith(Domain(r.offset)))) {
660  return false;
661  }
662 
663  return true;
664 }
665 
667  for (auto& ref_map : var_to_constraints_) {
668  ref_map.erase(kAffineRelationConstraint);
669  }
670 }
671 
672 // We only call that for a non representative variable that is only used in
673 // the kAffineRelationConstraint. Such variable can be ignored and should never
674 // be seen again in the presolve.
676  const int rep = GetAffineRelation(var).representative;
677 
679  CHECK_NE(var, rep);
680  CHECK_EQ(var_to_constraints_[var].size(), 1);
681  CHECK(var_to_constraints_[var].contains(kAffineRelationConstraint));
682  CHECK(var_to_constraints_[rep].contains(kAffineRelationConstraint));
683 
684  // We shouldn't reuse this variable again!
686 
687  var_to_constraints_[var].erase(kAffineRelationConstraint);
688  affine_relations_.IgnoreFromClassSize(var);
689  var_equiv_relations_.IgnoreFromClassSize(var);
690 
691  // If the representative is left alone, we can remove it from the special
692  // affine relation constraint too.
693  if (affine_relations_.ClassSize(rep) == 1 &&
694  var_equiv_relations_.ClassSize(rep) == 1) {
695  var_to_constraints_[rep].erase(kAffineRelationConstraint);
696  }
697 
698  if (VLOG_IS_ON(2)) {
699  LOG(INFO) << "Removing affine relation: " << AffineRelationDebugString(var);
700  }
701 }
702 
703 bool PresolveContext::StoreAffineRelation(int ref_x, int ref_y, int64_t coeff,
704  int64_t offset) {
705  CHECK_NE(coeff, 0);
706  if (is_unsat_) return false;
707 
708  // TODO(user): I am not 100% sure why, but sometimes the representative is
709  // fixed but that is not propagated to ref_x or ref_y and this causes issues.
710  if (!PropagateAffineRelation(ref_x)) return true;
711  if (!PropagateAffineRelation(ref_y)) return true;
712 
713  if (IsFixed(ref_x)) {
714  const int64_t lhs = DomainOf(ref_x).Min() - offset;
715  if (lhs % std::abs(coeff) != 0) {
716  is_unsat_ = true;
717  return true;
718  }
719  static_cast<void>(IntersectDomainWith(ref_y, Domain(lhs / coeff)));
720  UpdateRuleStats("affine: fixed");
721  return true;
722  }
723 
724  if (IsFixed(ref_y)) {
725  const int64_t value_x = DomainOf(ref_y).Min() * coeff + offset;
726  static_cast<void>(IntersectDomainWith(ref_x, Domain(value_x)));
727  UpdateRuleStats("affine: fixed");
728  return true;
729  }
730 
731  // If both are already in the same class, we need to make sure the relations
732  // are compatible.
735  if (rx.representative == ry.representative) {
736  // x = rx.coeff * rep + rx.offset;
737  // y = ry.coeff * rep + ry.offset_y;
738  // And x == coeff * ry.coeff * rep + (coeff * ry.offset + offset).
739  //
740  // So we get the relation a * rep == b with a and b defined here:
741  const int64_t a = coeff * ry.coeff - rx.coeff;
742  const int64_t b = coeff * ry.offset + offset - rx.offset;
743  if (a == 0) {
744  if (b != 0) is_unsat_ = true;
745  return true;
746  }
747  if (b % a != 0) {
748  is_unsat_ = true;
749  return true;
750  }
751  UpdateRuleStats("affine: unique solution");
752  const int64_t unique_value = -b / a;
753  if (!IntersectDomainWith(rx.representative, Domain(unique_value))) {
754  return true;
755  }
756  if (!IntersectDomainWith(ref_x,
757  Domain(unique_value * rx.coeff + rx.offset))) {
758  return true;
759  }
760  if (!IntersectDomainWith(ref_y,
761  Domain(unique_value * ry.coeff + ry.offset))) {
762  return true;
763  }
764  return true;
765  }
766 
767  const int x = PositiveRef(ref_x);
768  const int y = PositiveRef(ref_y);
769  const int64_t c =
770  RefIsPositive(ref_x) == RefIsPositive(ref_y) ? coeff : -coeff;
771  const int64_t o = RefIsPositive(ref_x) ? offset : -offset;
772 
773  // TODO(user): can we force the rep and remove GetAffineRelation()?
774  bool added = AddRelation(x, y, c, o, &affine_relations_);
775  if ((c == 1 || c == -1) && o == 0) {
776  added |= AddRelation(x, y, c, o, &var_equiv_relations_);
777  }
778  if (added) {
779  UpdateRuleStats("affine: new relation");
780 
781  // Lets propagate again the new relation. We might as well do it as early
782  // as possible and not all call site do it.
783  if (!PropagateAffineRelation(ref_x)) return true;
784  if (!PropagateAffineRelation(ref_y)) return true;
785 
786  // These maps should only contains representative, so only need to remap
787  // either x or y.
788  const int rep = GetAffineRelation(x).representative;
789  if (x != rep) encoding_remap_queue_.push_back(x);
790  if (y != rep) encoding_remap_queue_.push_back(y);
791 
792  // The domain didn't change, but this notification allows to re-process any
793  // constraint containing these variables. Note that we do not need to
794  // retrigger a propagation of the constraint containing a variable whose
795  // representative didn't change.
796  if (x != rep) modified_domains.Set(x);
797  if (y != rep) modified_domains.Set(y);
798 
799  var_to_constraints_[x].insert(kAffineRelationConstraint);
800  var_to_constraints_[y].insert(kAffineRelationConstraint);
801  return true;
802  }
803 
804  UpdateRuleStats("affine: incompatible relation");
805  if (VLOG_IS_ON(1)) {
806  LOG(INFO) << "Cannot add relation " << DomainOf(ref_x) << " = " << coeff
807  << " * " << DomainOf(ref_y) << " + " << offset
808  << " because of incompatibilities with existing relation: ";
809  for (const int ref : {ref_x, ref_y}) {
810  const auto r = GetAffineRelation(ref);
811  LOG(INFO) << DomainOf(ref) << " = " << r.coeff << " * "
812  << DomainOf(r.representative) << " + " << r.offset;
813  }
814  }
815 
816  return false;
817 }
818 
820  if (is_unsat_) return;
821 
822  CHECK(!VariableWasRemoved(ref_a));
823  CHECK(!VariableWasRemoved(ref_b));
824  CHECK(!DomainOf(ref_a).IsEmpty());
825  CHECK(!DomainOf(ref_b).IsEmpty());
826  CHECK(CanBeUsedAsLiteral(ref_a));
827  CHECK(CanBeUsedAsLiteral(ref_b));
828 
829  if (ref_a == ref_b) return;
830  if (ref_a == NegatedRef(ref_b)) {
831  is_unsat_ = true;
832  return;
833  }
834  const int var_a = PositiveRef(ref_a);
835  const int var_b = PositiveRef(ref_b);
836  if (RefIsPositive(ref_a) == RefIsPositive(ref_b)) {
837  // a = b
838  CHECK(StoreAffineRelation(var_a, var_b, /*coeff=*/1, /*offset=*/0));
839  } else {
840  // a = 1 - b
841  CHECK(StoreAffineRelation(var_a, var_b, /*coeff=*/-1, /*offset=*/1));
842  }
843 }
844 
845 bool PresolveContext::StoreAbsRelation(int target_ref, int ref) {
846  const auto insert_status = abs_relations_.insert(
847  std::make_pair(target_ref, SavedVariable(PositiveRef(ref))));
848  if (!insert_status.second) {
849  // Tricky: overwrite if the old value refer to a now unused variable.
850  const int candidate = insert_status.first->second.Get(this);
851  if (removed_variables_.contains(candidate)) {
852  insert_status.first->second = SavedVariable(PositiveRef(ref));
853  return true;
854  }
855  return false;
856  }
857  return true;
858 }
859 
860 bool PresolveContext::GetAbsRelation(int target_ref, int* ref) {
861  auto it = abs_relations_.find(target_ref);
862  if (it == abs_relations_.end()) return false;
863 
864  // Tricky: In some rare case the stored relation can refer to a deleted
865  // variable, so we need to ignore it.
866  //
867  // TODO(user): Incorporate this as part of SavedVariable/SavedLiteral so we
868  // make sure we never forget about this.
869  const int candidate = it->second.Get(this);
870  if (removed_variables_.contains(candidate)) {
871  abs_relations_.erase(it);
872  return false;
873  }
874  *ref = candidate;
875  CHECK(!VariableWasRemoved(*ref));
876  return true;
877 }
878 
881 
884  // Note(user): This can happen is some corner cases where the affine
885  // relation where added before the variable became usable as Boolean. When
886  // this is the case, the domain will be of the form [x, x + 1] and should be
887  // later remapped to a Boolean variable.
888  return ref;
889  }
890 
891  // We made sure that the affine representative can always be used as a
892  // literal. However, if some variable are fixed, we might not have only
893  // (coeff=1 offset=0) or (coeff=-1 offset=1) and we might have something like
894  // (coeff=8 offset=0) which is only valid for both variable at zero...
895  //
896  // What is sure is that depending on the value, only one mapping can be valid
897  // because r.coeff can never be zero.
898  const bool positive_possible = (r.offset == 0 || r.coeff + r.offset == 1);
899  const bool negative_possible = (r.offset == 1 || r.coeff + r.offset == 0);
900  DCHECK_NE(positive_possible, negative_possible);
901  if (RefIsPositive(ref)) {
902  return positive_possible ? r.representative : NegatedRef(r.representative);
903  } else {
904  return positive_possible ? NegatedRef(r.representative) : r.representative;
905  }
906 }
907 
909  const AffineRelation::Relation r = var_equiv_relations_.Get(PositiveRef(ref));
910  CHECK_EQ(std::abs(r.coeff), 1);
911  CHECK_EQ(r.offset, 0);
912  return RefIsPositive(ref) == (r.coeff == 1) ? r.representative
914 }
915 
916 // This makes sure that the affine relation only uses one of the
917 // representative from the var_equiv_relations_.
919  AffineRelation::Relation r = affine_relations_.Get(PositiveRef(ref));
920  AffineRelation::Relation o = var_equiv_relations_.Get(r.representative);
922  if (o.coeff == -1) r.coeff = -r.coeff;
923  if (!RefIsPositive(ref)) {
924  r.coeff *= -1;
925  r.offset *= -1;
926  }
927  return r;
928 }
929 
930 std::string PresolveContext::RefDebugString(int ref) const {
931  return absl::StrCat(RefIsPositive(ref) ? "X" : "-X", PositiveRef(ref),
932  DomainOf(ref).ToString());
933 }
934 
935 std::string PresolveContext::AffineRelationDebugString(int ref) const {
937  return absl::StrCat(RefDebugString(ref), " = ", r.coeff, " * ",
938  RefDebugString(r.representative), " + ", r.offset);
939 }
940 
941 // Create the internal structure for any new variables in working_model.
943  for (int i = domains.size(); i < working_model->variables_size(); ++i) {
944  domains.emplace_back(ReadDomainFromProto(working_model->variables(i)));
945  if (domains.back().IsEmpty()) {
946  is_unsat_ = true;
947  return;
948  }
949  if (IsFixed(i)) ExploitFixedDomain(i);
950  }
951  modified_domains.Resize(domains.size());
952  var_to_constraints_.resize(domains.size());
953  var_to_num_linear1_.resize(domains.size());
954  var_to_ub_only_constraints.resize(domains.size());
955  var_to_lb_only_constraints.resize(domains.size());
956 }
957 
958 bool PresolveContext::RemapEncodingMaps() {
959  // TODO(user): for now, while the code works most of the time, it triggers
960  // weird side effect that causes some issues in some LNS presolve...
961  // We should continue the investigation before activating it.
962  //
963  // Note also that because all our encoding constraints are present in the
964  // model, they will be remapped, and the new mapping re-added again. So while
965  // the current code might not be efficient, it should eventually reach the
966  // same effect.
967  encoding_remap_queue_.clear();
968 
969  // Note that InsertVarValueEncodingInternal() will potentially add new entry
970  // to the encoding_ map, but for a different variables. So this code relies on
971  // the fact that the var_map shouldn't change content nor address of the
972  // "var_map" below while we iterate on them.
973  for (const int var : encoding_remap_queue_) {
976  if (r.representative == var) return true;
977  int num_remapping = 0;
978 
979  // Encoding.
980  {
981  const absl::flat_hash_map<int64_t, SavedLiteral>& var_map =
982  encoding_[var];
983  for (const auto& entry : var_map) {
984  const int lit = entry.second.Get(this);
985  if (removed_variables_.contains(PositiveRef(lit))) continue;
986  if ((entry.first - r.offset) % r.coeff != 0) continue;
987  const int64_t rep_value = (entry.first - r.offset) / r.coeff;
988  ++num_remapping;
989  InsertVarValueEncodingInternal(lit, r.representative, rep_value,
990  /*add_constraints=*/false);
991  if (is_unsat_) return false;
992  }
993  encoding_.erase(var);
994  }
995 
996  // Eq half encoding.
997  {
998  const absl::flat_hash_map<int64_t, absl::flat_hash_set<int>>& var_map =
999  eq_half_encoding_[var];
1000  for (const auto& entry : var_map) {
1001  if ((entry.first - r.offset) % r.coeff != 0) continue;
1002  const int64_t rep_value = (entry.first - r.offset) / r.coeff;
1003  for (int literal : entry.second) {
1004  ++num_remapping;
1005  InsertHalfVarValueEncoding(GetLiteralRepresentative(literal),
1006  r.representative, rep_value,
1007  /*imply_eq=*/true);
1008  if (is_unsat_) return false;
1009  }
1010  }
1011  eq_half_encoding_.erase(var);
1012  }
1013 
1014  // Neq half encoding.
1015  {
1016  const absl::flat_hash_map<int64_t, absl::flat_hash_set<int>>& var_map =
1017  neq_half_encoding_[var];
1018  for (const auto& entry : var_map) {
1019  if ((entry.first - r.offset) % r.coeff != 0) continue;
1020  const int64_t rep_value = (entry.first - r.offset) / r.coeff;
1021  for (int literal : entry.second) {
1022  ++num_remapping;
1023  InsertHalfVarValueEncoding(GetLiteralRepresentative(literal),
1024  r.representative, rep_value,
1025  /*imply_eq=*/false);
1026  if (is_unsat_) return false;
1027  }
1028  }
1029  neq_half_encoding_.erase(var);
1030  }
1031 
1032  if (num_remapping > 0) {
1033  VLOG(1) << "Remapped " << num_remapping << " encodings due to " << var
1034  << " -> " << r.representative << ".";
1035  }
1036  }
1037  encoding_remap_queue_.clear();
1038  return !is_unsat_;
1039 }
1040 
1043  CHECK_EQ(DomainOf(var).Size(), 2);
1044  const int64_t var_min = MinOf(var);
1045  const int64_t var_max = MaxOf(var);
1046 
1047  if (is_unsat_) return;
1048 
1049  absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[var];
1050 
1051  // Find encoding for min if present.
1052  auto min_it = var_map.find(var_min);
1053  if (min_it != var_map.end()) {
1054  const int old_var = PositiveRef(min_it->second.Get(this));
1055  if (removed_variables_.contains(old_var)) {
1056  var_map.erase(min_it);
1057  min_it = var_map.end();
1058  }
1059  }
1060 
1061  // Find encoding for max if present.
1062  auto max_it = var_map.find(var_max);
1063  if (max_it != var_map.end()) {
1064  const int old_var = PositiveRef(max_it->second.Get(this));
1065  if (removed_variables_.contains(old_var)) {
1066  var_map.erase(max_it);
1067  max_it = var_map.end();
1068  }
1069  }
1070 
1071  // Insert missing encoding.
1072  int min_literal;
1073  int max_literal;
1074  if (min_it != var_map.end() && max_it != var_map.end()) {
1075  min_literal = min_it->second.Get(this);
1076  max_literal = max_it->second.Get(this);
1077  if (min_literal != NegatedRef(max_literal)) {
1078  UpdateRuleStats("variables with 2 values: merge encoding literals");
1079  StoreBooleanEqualityRelation(min_literal, NegatedRef(max_literal));
1080  if (is_unsat_) return;
1081  }
1082  min_literal = GetLiteralRepresentative(min_literal);
1083  max_literal = GetLiteralRepresentative(max_literal);
1084  if (!IsFixed(min_literal)) CHECK_EQ(min_literal, NegatedRef(max_literal));
1085  } else if (min_it != var_map.end() && max_it == var_map.end()) {
1086  UpdateRuleStats("variables with 2 values: register other encoding");
1087  min_literal = min_it->second.Get(this);
1088  max_literal = NegatedRef(min_literal);
1089  var_map[var_max] = SavedLiteral(max_literal);
1090  } else if (min_it == var_map.end() && max_it != var_map.end()) {
1091  UpdateRuleStats("variables with 2 values: register other encoding");
1092  max_literal = max_it->second.Get(this);
1093  min_literal = NegatedRef(max_literal);
1094  var_map[var_min] = SavedLiteral(min_literal);
1095  } else {
1096  UpdateRuleStats("variables with 2 values: create encoding literal");
1097  max_literal = NewBoolVar();
1098  min_literal = NegatedRef(max_literal);
1099  var_map[var_min] = SavedLiteral(min_literal);
1100  var_map[var_max] = SavedLiteral(max_literal);
1101  }
1102 
1103  if (IsFixed(min_literal) || IsFixed(max_literal)) {
1104  CHECK(IsFixed(min_literal));
1105  CHECK(IsFixed(max_literal));
1106  UpdateRuleStats("variables with 2 values: fixed encoding");
1107  if (LiteralIsTrue(min_literal)) {
1108  return static_cast<void>(IntersectDomainWith(var, Domain(var_min)));
1109  } else {
1110  return static_cast<void>(IntersectDomainWith(var, Domain(var_max)));
1111  }
1112  }
1113 
1114  // Add affine relation.
1115  if (GetAffineRelation(var).representative != PositiveRef(min_literal)) {
1116  UpdateRuleStats("variables with 2 values: new affine relation");
1117  if (RefIsPositive(max_literal)) {
1118  CHECK(StoreAffineRelation(var, PositiveRef(max_literal),
1119  var_max - var_min, var_min));
1120  } else {
1121  CHECK(StoreAffineRelation(var, PositiveRef(max_literal),
1122  var_min - var_max, var_max));
1123  }
1124  }
1125 }
1126 
1127 void PresolveContext::InsertVarValueEncodingInternal(int literal, int var,
1128  int64_t value,
1129  bool add_constraints) {
1132  absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[var];
1133 
1134  // Ticky and rare: I have only observed this on the LNS of
1135  // radiation_m18_12_05_sat.fzn. The value was encoded, but maybe we never
1136  // used the involved variables / constraints, so it was removed (with the
1137  // encoding constraints) from the model already! We have to be careful.
1138  const auto it = var_map.find(value);
1139  if (it != var_map.end()) {
1140  const int old_var = PositiveRef(it->second.Get(this));
1141  if (removed_variables_.contains(old_var)) {
1142  var_map.erase(it);
1143  }
1144  }
1145 
1146  const auto insert =
1147  var_map.insert(std::make_pair(value, SavedLiteral(literal)));
1148 
1149  // If an encoding already exist, make the two Boolean equals.
1150  if (!insert.second) {
1151  const int previous_literal = insert.first->second.Get(this);
1152  CHECK(!VariableWasRemoved(previous_literal));
1153  if (literal != previous_literal) {
1155  "variables: merge equivalent var value encoding literals");
1156  StoreBooleanEqualityRelation(literal, previous_literal);
1157  }
1158  return;
1159  }
1160 
1161  if (DomainOf(var).Size() == 2) {
1163  } else {
1164  VLOG(2) << "Insert lit(" << literal << ") <=> var(" << var
1165  << ") == " << value;
1166  eq_half_encoding_[var][value].insert(literal);
1167  neq_half_encoding_[var][value].insert(NegatedRef(literal));
1168  if (add_constraints) {
1169  UpdateRuleStats("variables: add encoding constraint");
1170  AddImplyInDomain(literal, var, Domain(value));
1171  AddImplyInDomain(NegatedRef(literal), var, Domain(value).Complement());
1172  }
1173  }
1174 }
1175 
1176 bool PresolveContext::InsertHalfVarValueEncoding(int literal, int var,
1177  int64_t value, bool imply_eq) {
1178  if (is_unsat_) return false;
1180 
1181  // Creates the linking sets on demand.
1182  // Insert the enforcement literal in the half encoding map.
1183  auto& direct_set =
1184  imply_eq ? eq_half_encoding_[var][value] : neq_half_encoding_[var][value];
1185  if (!direct_set.insert(literal).second) return false; // Already there.
1186 
1187  VLOG(2) << "Collect lit(" << literal << ") implies var(" << var
1188  << (imply_eq ? ") == " : ") != ") << value;
1189  UpdateRuleStats("variables: detect half reified value encoding");
1190 
1191  // Note(user): We don't expect a lot of literals in these sets, so doing
1192  // a scan should be okay.
1193  auto& other_set =
1194  imply_eq ? neq_half_encoding_[var][value] : eq_half_encoding_[var][value];
1195  for (const int other : other_set) {
1196  if (GetLiteralRepresentative(other) != NegatedRef(literal)) continue;
1197 
1198  UpdateRuleStats("variables: detect fully reified value encoding");
1199  const int imply_eq_literal = imply_eq ? literal : NegatedRef(literal);
1200  InsertVarValueEncodingInternal(imply_eq_literal, var, value,
1201  /*add_constraints=*/false);
1202  break;
1203  }
1204 
1205  return true;
1206 }
1207 
1208 bool PresolveContext::CanonicalizeEncoding(int* ref, int64_t* value) {
1209  const AffineRelation::Relation r = GetAffineRelation(*ref);
1210  if ((*value - r.offset) % r.coeff != 0) return false;
1211  *ref = r.representative;
1212  *value = (*value - r.offset) / r.coeff;
1213  return true;
1214 }
1215 
1217  int64_t value) {
1218  if (!RemapEncodingMaps()) return;
1219  if (!CanonicalizeEncoding(&ref, &value)) return;
1221  InsertVarValueEncodingInternal(literal, ref, value, /*add_constraints=*/true);
1222 }
1223 
1225  int64_t value) {
1226  if (!RemapEncodingMaps()) return false;
1227  if (!CanonicalizeEncoding(&var, &value)) return false;
1229  return InsertHalfVarValueEncoding(literal, var, value, /*imply_eq=*/true);
1230 }
1231 
1233  int64_t value) {
1234  if (!RemapEncodingMaps()) return false;
1235  if (!CanonicalizeEncoding(&var, &value)) return false;
1237  return InsertHalfVarValueEncoding(literal, var, value, /*imply_eq=*/false);
1238 }
1239 
1241  int* literal) {
1242  CHECK(!VariableWasRemoved(ref));
1243  if (!RemapEncodingMaps()) return false;
1244  if (!CanonicalizeEncoding(&ref, &value)) return false;
1245  const absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[ref];
1246  const auto it = var_map.find(value);
1247  if (it != var_map.end()) {
1248  if (literal != nullptr) {
1249  *literal = it->second.Get(this);
1250  }
1251  return true;
1252  }
1253  return false;
1254 }
1255 
1257  // TODO(user): Remove this precondition. For now it is needed because
1258  // we might remove encoding literal without updating the encoding map.
1259  // This is related to RemapEncodingMaps() which is currently disabled.
1260  CHECK(!ModelIsExpanded());
1261 
1262  CHECK(!VariableWasRemoved(ref));
1263  if (!RemapEncodingMaps()) return GetOrCreateConstantVar(0);
1264  if (!CanonicalizeEncoding(&ref, &value)) return GetOrCreateConstantVar(0);
1265 
1266  // Positive after CanonicalizeEncoding().
1267  const int var = ref;
1268 
1269  // Returns the false literal if the value is not in the domain.
1270  if (!domains[var].Contains(value)) {
1271  return GetOrCreateConstantVar(0);
1272  }
1273 
1274  // Returns the associated literal if already present.
1275  absl::flat_hash_map<int64_t, SavedLiteral>& var_map = encoding_[var];
1276  auto it = var_map.find(value);
1277  if (it != var_map.end()) {
1278  return it->second.Get(this);
1279  }
1280 
1281  // Special case for fixed domains.
1282  if (domains[var].Size() == 1) {
1283  const int true_literal = GetOrCreateConstantVar(1);
1284  var_map[value] = SavedLiteral(true_literal);
1285  return true_literal;
1286  }
1287 
1288  // Special case for domains of size 2.
1289  const int64_t var_min = MinOf(var);
1290  const int64_t var_max = MaxOf(var);
1291  if (domains[var].Size() == 2) {
1292  // Checks if the other value is already encoded.
1293  const int64_t other_value = value == var_min ? var_max : var_min;
1294  auto other_it = var_map.find(other_value);
1295  if (other_it != var_map.end()) {
1296  // Update the encoding map. The domain could have been reduced to size
1297  // two after the creation of the first literal.
1298  const int literal = NegatedRef(other_it->second.Get(this));
1299  var_map[value] = SavedLiteral(literal);
1300  return literal;
1301  }
1302 
1303  if (var_min == 0 && var_max == 1) {
1305  var_map[1] = SavedLiteral(representative);
1306  var_map[0] = SavedLiteral(NegatedRef(representative));
1307  return value == 1 ? representative : NegatedRef(representative);
1308  } else {
1309  const int literal = NewBoolVar();
1310  InsertVarValueEncoding(literal, var, var_max);
1312  return value == var_max ? representative : NegatedRef(representative);
1313  }
1314  }
1315 
1316  const int literal = NewBoolVar();
1319 }
1320 
1322  const CpObjectiveProto& obj = working_model->objective();
1323 
1324  objective_offset_ = obj.offset();
1325  objective_scaling_factor_ = obj.scaling_factor();
1326  if (objective_scaling_factor_ == 0.0) {
1327  objective_scaling_factor_ = 1.0;
1328  }
1329  if (!obj.domain().empty()) {
1330  // We might relax this in CanonicalizeObjective() when we will compute
1331  // the possible objective domain from the domains of the variables.
1332  objective_domain_is_constraining_ = true;
1333  objective_domain_ = ReadDomainFromProto(obj);
1334  } else {
1335  objective_domain_is_constraining_ = false;
1336  objective_domain_ = Domain::AllValues();
1337  }
1338 
1339  // This is an upper bound of the higher magnitude that can be reach by
1340  // summing an objective partial sum. Because of the model validation, this
1341  // shouldn't overflow, and we make sure it stays this way.
1342  objective_overflow_detection_ = 0;
1343 
1344  objective_map_.clear();
1345  for (int i = 0; i < obj.vars_size(); ++i) {
1346  const int ref = obj.vars(i);
1347  int64_t coeff = obj.coeffs(i);
1348  if (!RefIsPositive(ref)) coeff = -coeff;
1349  int var = PositiveRef(ref);
1350 
1351  objective_overflow_detection_ +=
1352  std::abs(coeff) * std::max(std::abs(MinOf(var)), std::abs(MaxOf(var)));
1353 
1354  objective_map_[var] += coeff;
1355  if (objective_map_[var] == 0) {
1356  objective_map_.erase(var);
1357  var_to_constraints_[var].erase(kObjectiveConstraint);
1358  } else {
1359  var_to_constraints_[var].insert(kObjectiveConstraint);
1360  }
1361  }
1362 }
1363 
1365  int64_t offset_change = 0;
1366 
1367  // We replace each entry by its affine representative.
1368  // Note that the non-deterministic loop is fine, but because we iterate
1369  // one the map while modifying it, it is safer to do a copy rather than to
1370  // try to handle that in one pass.
1371  tmp_entries_.clear();
1372  for (const auto& entry : objective_map_) {
1373  tmp_entries_.push_back(entry);
1374  }
1375 
1376  // TODO(user): This is a bit duplicated with the presolve linear code.
1377  // We also do not propagate back any domain restriction from the objective to
1378  // the variables if any.
1379  for (const auto& entry : tmp_entries_) {
1380  const int var = entry.first;
1381  const auto it = objective_map_.find(var);
1382  if (it == objective_map_.end()) continue;
1383  const int64_t coeff = it->second;
1384 
1385  // If a variable only appear in objective, we can fix it!
1386  // Note that we don't care if it was in affine relation, because if none
1387  // of the relations are left, then we can still fix it.
1388  if (!keep_all_feasible_solutions && !objective_domain_is_constraining_ &&
1390  var_to_constraints_[var].size() == 1 &&
1391  var_to_constraints_[var].contains(kObjectiveConstraint)) {
1392  UpdateRuleStats("objective: variable not used elsewhere");
1393  if (coeff > 0) {
1394  if (!IntersectDomainWith(var, Domain(MinOf(var)))) {
1395  return false;
1396  }
1397  } else {
1398  if (!IntersectDomainWith(var, Domain(MaxOf(var)))) {
1399  return false;
1400  }
1401  }
1402  }
1403 
1404  if (IsFixed(var)) {
1405  offset_change += coeff * MinOf(var);
1406  var_to_constraints_[var].erase(kObjectiveConstraint);
1407  objective_map_.erase(var);
1408  continue;
1409  }
1410 
1412  if (r.representative == var) continue;
1413 
1414  objective_map_.erase(var);
1415  var_to_constraints_[var].erase(kObjectiveConstraint);
1416 
1417  // Do the substitution.
1418  offset_change += coeff * r.offset;
1419  const int64_t new_coeff = objective_map_[r.representative] +=
1420  coeff * r.coeff;
1421 
1422  // Process new term.
1423  if (new_coeff == 0) {
1424  objective_map_.erase(r.representative);
1425  var_to_constraints_[r.representative].erase(kObjectiveConstraint);
1426  } else {
1427  var_to_constraints_[r.representative].insert(kObjectiveConstraint);
1428  if (IsFixed(r.representative)) {
1429  offset_change += new_coeff * MinOf(r.representative);
1430  var_to_constraints_[r.representative].erase(kObjectiveConstraint);
1431  objective_map_.erase(r.representative);
1432  }
1433  }
1434  }
1435 
1436  Domain implied_domain(0);
1437  int64_t gcd(0);
1438 
1439  // We need to sort the entries to be deterministic.
1440  tmp_entries_.clear();
1441  for (const auto& entry : objective_map_) {
1442  tmp_entries_.push_back(entry);
1443  }
1444  std::sort(tmp_entries_.begin(), tmp_entries_.end());
1445  for (const auto& entry : tmp_entries_) {
1446  const int var = entry.first;
1447  const int64_t coeff = entry.second;
1448  gcd = MathUtil::GCD64(gcd, std::abs(coeff));
1449  implied_domain =
1450  implied_domain.AdditionWith(DomainOf(var).MultiplicationBy(coeff))
1451  .RelaxIfTooComplex();
1452  }
1453 
1454  // This is the new domain.
1455  // Note that the domain never include the offset.
1456  objective_domain_ = objective_domain_.AdditionWith(Domain(-offset_change))
1457  .IntersectionWith(implied_domain);
1458  objective_domain_ =
1459  objective_domain_.SimplifyUsingImpliedDomain(implied_domain);
1460 
1461  // Updat the offset.
1462  objective_offset_ += offset_change;
1463 
1464  // Maybe divide by GCD.
1465  if (gcd > 1) {
1466  for (auto& entry : objective_map_) {
1467  entry.second /= gcd;
1468  }
1469  objective_domain_ = objective_domain_.InverseMultiplicationBy(gcd);
1470  objective_offset_ /= static_cast<double>(gcd);
1471  objective_scaling_factor_ *= static_cast<double>(gcd);
1472  }
1473 
1474  if (objective_domain_.IsEmpty()) return false;
1475 
1476  // Detect if the objective domain do not limit the "optimal" objective value.
1477  // If this is true, then we can apply any reduction that reduce the objective
1478  // value without any issues.
1479  objective_domain_is_constraining_ =
1480  !implied_domain
1482  objective_domain_.Max()))
1483  .IsIncludedIn(objective_domain_);
1484  return true;
1485 }
1486 
1488  objective_map_.erase(var);
1489  var_to_constraints_[var].erase(kObjectiveConstraint);
1490 }
1491 
1493  int64_t& map_ref = objective_map_[var];
1494  map_ref += value;
1495  if (map_ref == 0) {
1496  objective_map_.erase(var);
1497  var_to_constraints_[var].erase(kObjectiveConstraint);
1498  } else {
1499  var_to_constraints_[var].insert(kObjectiveConstraint);
1500  }
1501 }
1502 
1504  // Tricky: The objective domain is without the offset, so we need to shift it.
1505  objective_offset_ += static_cast<double>(value);
1506  objective_domain_ = objective_domain_.AdditionWith(Domain(-value));
1507 }
1508 
1510  int var_in_equality, int64_t coeff_in_equality,
1511  const ConstraintProto& equality, std::vector<int>* new_vars_in_objective) {
1512  CHECK(equality.enforcement_literal().empty());
1513  CHECK(RefIsPositive(var_in_equality));
1514 
1515  if (new_vars_in_objective != nullptr) new_vars_in_objective->clear();
1516 
1517  // We can only "easily" substitute if the objective coefficient is a multiple
1518  // of the one in the constraint.
1519  const int64_t coeff_in_objective =
1520  gtl::FindOrDie(objective_map_, var_in_equality);
1521  CHECK_NE(coeff_in_equality, 0);
1522  CHECK_EQ(coeff_in_objective % coeff_in_equality, 0);
1523  const int64_t multiplier = coeff_in_objective / coeff_in_equality;
1524 
1525  // Abort if the new objective seems to violate our overflow preconditions.
1526  int64_t change = 0;
1527  for (int i = 0; i < equality.linear().vars().size(); ++i) {
1528  int var = equality.linear().vars(i);
1529  if (PositiveRef(var) == var_in_equality) continue;
1530  int64_t coeff = equality.linear().coeffs(i);
1531  change +=
1532  std::abs(coeff) * std::max(std::abs(MinOf(var)), std::abs(MaxOf(var)));
1533  }
1534  const int64_t new_value =
1535  CapAdd(CapProd(std::abs(multiplier), change),
1536  objective_overflow_detection_ -
1537  std::abs(coeff_in_equality) *
1538  std::max(std::abs(MinOf(var_in_equality)),
1539  std::abs(MaxOf(var_in_equality))));
1540  if (new_value == std::numeric_limits<int64_t>::max()) return false;
1541  objective_overflow_detection_ = new_value;
1542 
1543  for (int i = 0; i < equality.linear().vars().size(); ++i) {
1544  int var = equality.linear().vars(i);
1545  int64_t coeff = equality.linear().coeffs(i);
1546  if (!RefIsPositive(var)) {
1547  var = NegatedRef(var);
1548  coeff = -coeff;
1549  }
1550  if (var == var_in_equality) continue;
1551 
1552  int64_t& map_ref = objective_map_[var];
1553  if (map_ref == 0 && new_vars_in_objective != nullptr) {
1554  new_vars_in_objective->push_back(var);
1555  }
1556  map_ref -= coeff * multiplier;
1557 
1558  if (map_ref == 0) {
1559  objective_map_.erase(var);
1560  var_to_constraints_[var].erase(kObjectiveConstraint);
1561  } else {
1562  var_to_constraints_[var].insert(kObjectiveConstraint);
1563  }
1564  }
1565 
1566  objective_map_.erase(var_in_equality);
1567  var_to_constraints_[var_in_equality].erase(kObjectiveConstraint);
1568 
1569  // Deal with the offset.
1570  Domain offset = ReadDomainFromProto(equality.linear());
1571  DCHECK_EQ(offset.Min(), offset.Max());
1572  bool exact = true;
1573  offset = offset.MultiplicationBy(multiplier, &exact);
1574  CHECK(exact);
1575  CHECK(!offset.IsEmpty());
1576 
1577  // Tricky: The objective domain is without the offset, so we need to shift it.
1578  objective_offset_ += static_cast<double>(offset.Min());
1579  objective_domain_ = objective_domain_.AdditionWith(Domain(-offset.Min()));
1580 
1581  // Because we can assume that the constraint we used was constraining
1582  // (otherwise it would have been removed), the objective domain should be now
1583  // constraining.
1584  objective_domain_is_constraining_ = true;
1585 
1586  if (objective_domain_.IsEmpty()) {
1587  return NotifyThatModelIsUnsat();
1588  }
1589  return true;
1590 }
1591 
1593  absl::Span<const int> exactly_one) {
1594  int64_t min_coeff = std::numeric_limits<int64_t>::max();
1595  for (const int ref : exactly_one) {
1596  const auto it = objective_map_.find(PositiveRef(ref));
1597  if (it == objective_map_.end()) return false;
1598 
1599  const int64_t coeff = it->second;
1600  if (RefIsPositive(ref)) {
1601  min_coeff = std::min(min_coeff, coeff);
1602  } else {
1603  // Objective = coeff * var = coeff * (1 - ref);
1604  min_coeff = std::min(min_coeff, -coeff);
1605  }
1606  }
1607 
1608  int64_t offset = min_coeff;
1609  for (const int ref : exactly_one) {
1610  const int var = PositiveRef(ref);
1611  int64_t& map_ref = objective_map_.at(var);
1612  if (RefIsPositive(ref)) {
1613  map_ref -= min_coeff;
1614  if (map_ref == 0) {
1615  objective_map_.erase(var);
1616  var_to_constraints_[var].erase(kObjectiveConstraint);
1617  }
1618  } else {
1619  // Term = coeff * (1 - X) = coeff - coeff * X;
1620  // So -coeff -> -coeff -min_coeff
1621  // And Term = coeff + min_coeff - min_coeff - (coeff + min_coeff) * X
1622  // = (coeff + min_coeff) * (1 - X) - min_coeff;
1623  map_ref += min_coeff;
1624  if (map_ref == 0) {
1625  objective_map_.erase(var);
1626  var_to_constraints_[var].erase(kObjectiveConstraint);
1627  }
1628  offset -= min_coeff;
1629  }
1630  }
1631 
1632  // Note that the domain never include the offset, so we need to update it.
1633  if (offset != 0) {
1634  objective_offset_ += offset;
1635  objective_domain_ = objective_domain_.AdditionWith(Domain(-offset));
1636  }
1637 
1638  return true;
1639 }
1640 
1642  // We need to sort the entries to be deterministic.
1643  std::vector<std::pair<int, int64_t>> entries;
1644  for (const auto& entry : objective_map_) {
1645  entries.push_back(entry);
1646  }
1647  std::sort(entries.begin(), entries.end());
1648 
1650  mutable_obj->set_offset(objective_offset_);
1651  mutable_obj->set_scaling_factor(objective_scaling_factor_);
1652  FillDomainInProto(objective_domain_, mutable_obj);
1653  mutable_obj->clear_vars();
1654  mutable_obj->clear_coeffs();
1655  for (const auto& entry : entries) {
1656  mutable_obj->add_vars(entry.first);
1657  mutable_obj->add_coeffs(entry.second);
1658  }
1659 }
1660 
1662  int active_i,
1663  int active_j) {
1664  CHECK(!LiteralIsFalse(active_i));
1665  CHECK(!LiteralIsFalse(active_j));
1666 
1667  // Sort the active literals.
1668  if (active_j < active_i) std::swap(active_i, active_j);
1669 
1670  const std::tuple<int, int, int, int> key =
1671  std::make_tuple(time_i, time_j, active_i, active_j);
1672  const auto& it = reified_precedences_cache_.find(key);
1673  if (it != reified_precedences_cache_.end()) return it->second;
1674 
1675  const int result = NewBoolVar();
1676  reified_precedences_cache_[key] = result;
1677 
1678  // result => (time_i <= time_j) && active_i && active_j.
1679  ConstraintProto* const lesseq = working_model->add_constraints();
1680  lesseq->add_enforcement_literal(result);
1681  lesseq->mutable_linear()->add_vars(time_i);
1682  lesseq->mutable_linear()->add_vars(time_j);
1683  lesseq->mutable_linear()->add_coeffs(-1);
1684  lesseq->mutable_linear()->add_coeffs(1);
1685  lesseq->mutable_linear()->add_domain(0);
1687  if (!LiteralIsTrue(active_i)) {
1688  AddImplication(result, active_i);
1689  }
1690  if (!LiteralIsTrue(active_j)) {
1691  AddImplication(result, active_j);
1692  }
1693 
1694  // Not(result) && active_i && active_j => (time_i > time_j)
1695  ConstraintProto* const greater = working_model->add_constraints();
1696  greater->mutable_linear()->add_vars(time_i);
1697  greater->mutable_linear()->add_vars(time_j);
1698  greater->mutable_linear()->add_coeffs(-1);
1699  greater->mutable_linear()->add_coeffs(1);
1701  greater->mutable_linear()->add_domain(-1);
1702 
1703  // Manages enforcement literal.
1704  greater->add_enforcement_literal(NegatedRef(result));
1705  if (!LiteralIsTrue(active_i)) {
1706  greater->add_enforcement_literal(active_i);
1707  }
1708  if (!LiteralIsTrue(active_j)) {
1709  greater->add_enforcement_literal(active_j);
1710  }
1711 
1712  // This is redundant but should improves performance.
1713  //
1714  // If GetOrCreateReifiedPrecedenceLiteral(time_j, time_i, active_j, active_j)
1715  // (the reverse precedence) has been called too, then we can link the two
1716  // precedence literals, and the two active literals together.
1717  const auto& rev_it = reified_precedences_cache_.find(
1718  std::make_tuple(time_j, time_i, active_i, active_j));
1719  if (rev_it != reified_precedences_cache_.end()) {
1720  auto* const bool_or = working_model->add_constraints()->mutable_bool_or();
1721  bool_or->add_literals(result);
1722  bool_or->add_literals(rev_it->second);
1723  bool_or->add_literals(NegatedRef(active_i));
1724  bool_or->add_literals(NegatedRef(active_j));
1725  }
1726 
1727  return result;
1728 }
1729 
1731  reified_precedences_cache_.clear();
1732 }
1733 
1735  SOLVER_LOG(logger_, "");
1736  SOLVER_LOG(logger_, "Presolve summary:");
1737  SOLVER_LOG(logger_, " - ", NumAffineRelations(),
1738  " affine relations were detected.");
1739  SOLVER_LOG(logger_, " - ", NumEquivRelations(),
1740  " variable equivalence relations were detected.");
1741  std::map<std::string, int> sorted_rules(stats_by_rule_name_.begin(),
1742  stats_by_rule_name_.end());
1743  for (const auto& entry : sorted_rules) {
1744  if (entry.second == 1) {
1745  SOLVER_LOG(logger_, " - rule '", entry.first, "' was applied 1 time.");
1746  } else {
1747  SOLVER_LOG(logger_, " - rule '", entry.first, "' was applied ",
1748  entry.second, " times.");
1749  }
1750  }
1751 }
1752 
1753 } // namespace sat
1754 } // namespace operations_research
::PROTOBUF_NAMESPACE_ID::int64 offset() const
Definition: cp_model.pb.h:7038
std::vector< int > UsedVariables(const ConstraintProto &ct)
#define CHECK(condition)
Definition: base/logging.h:491
int Get(PresolveContext *context) const
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int32 > * mutable_vars()
Definition: cp_model.pb.h:7282
int64_t min
Definition: alldiff_cst.cc:139
void Set(IntegerType index)
Definition: bitset.h:804
::PROTOBUF_NAMESPACE_ID::int64 domain(int index) const
#define SOLVER_LOG(logger,...)
Definition: util/logging.h:63
void add_enforcement_literal(::PROTOBUF_NAMESPACE_ID::int32 value)
Definition: cp_model.pb.h:9275
Domain InverseMultiplicationBy(const int64_t coeff) const
Returns {x ∈ Int64, ∃ e ∈ D, x * coeff = e}.
void UpdateRuleStats(const std::string &name, int num_times=1)
void StoreBooleanEqualityRelation(int ref_a, int ref_b)
void AddToObjective(int var, int64_t value)
bool StoreLiteralImpliesVarNEqValue(int literal, int var, int64_t value)
#define VLOG(verboselevel)
Definition: base/logging.h:979
bool GetAbsRelation(int target_ref, int *ref)
const std::string name
bool StoreAffineRelation(int ref_x, int ref_y, int64_t coeff, int64_t offset)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:263
constexpr int kAffineRelationConstraint
void Resize(IntegerType size)
Definition: bitset.h:790
void add_coeffs(::PROTOBUF_NAMESPACE_ID::int64 value)
Definition: cp_model.pb.h:7311
const absl::string_view ToString(MPSolver::OptimizationProblemType optimization_problem_type)
#define LOG(severity)
Definition: base/logging.h:416
::PROTOBUF_NAMESPACE_ID::int64 coeffs(int index) const
Definition: cp_model.pb.h:7300
std::string AffineRelationDebugString(int ref) const
bool StoreAbsRelation(int target_ref, int ref)
int LiteralForExpressionMax(const LinearExpressionProto &expr) const
void add_literals(::PROTOBUF_NAMESPACE_ID::int32 value)
Definition: cp_model.pb.h:6839
int64_t CapProd(int64_t x, int64_t y)
bool ExpressionIsAffineBoolean(const LinearExpressionProto &expr) const
Domain SimplifyUsingImpliedDomain(const Domain &implied_domain) const
Advanced usage.
::operations_research::sat::IntegerVariableProto * add_variables()
bool VariableIsOnlyUsedInEncodingAndMaybeInObjective(int ref) const
int64_t Max() const
Returns the max value of the domain.
void add_vars(::PROTOBUF_NAMESPACE_ID::int32 value)
Definition: cp_model.pb.h:7264
static Domain AllValues()
Returns the full domain Int64.
void add_domain(::PROTOBUF_NAMESPACE_ID::int64 value)
Definition: cp_model.pb.h:7358
int64_t b
std::string ProtobufDebugString(const P &message)
ABSL_MUST_USE_RESULT bool SetLiteralToFalse(int lit)
::PROTOBUF_NAMESPACE_ID::int32 vars(int index) const
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int32 > * mutable_enforcement_literal()
Definition: cp_model.pb.h:9293
::PROTOBUF_NAMESPACE_ID::int32 enforcement_literal(int index) const
Definition: cp_model.pb.h:9264
::operations_research::sat::BoolArgumentProto * mutable_bool_or()
Definition: cp_model.pb.h:9366
const ::operations_research::sat::LinearConstraintProto & linear() const
int64_t max
Definition: alldiff_cst.cc:140
::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 > * mutable_coeffs()
Definition: cp_model.pb.h:7329
bool HasVarValueEncoding(int ref, int64_t value, int *literal=nullptr)
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
CpModelProto proto
int64_t Min() const
Returns the min value of the domain.
int64_t CapAdd(int64_t x, int64_t y)
Domain Negation() const
Returns {x ∈ Int64, ∃ e ∈ D, x = -e}.
::PROTOBUF_NAMESPACE_ID::int64 coeffs(int index) const
Definition: cp_model.pb.h:6997
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:887
#define CHECK_LE(val1, val2)
Definition: base/logging.h:700
::operations_research::sat::LinearConstraintProto * mutable_linear()
const ::operations_research::sat::ConstraintProto & constraints(int index) const
static int64_t GCD64(int64_t x, int64_t y)
Definition: mathutil.h:107
::operations_research::sat::ConstraintProto * add_constraints()
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:200
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:206
const ::operations_research::sat::CpObjectiveProto & objective() const
void add_domain(::PROTOBUF_NAMESPACE_ID::int64 value)
Definition: cp_model.pb.h:6788
::PROTOBUF_NAMESPACE_ID::int64 coeffs(int index) const
std::vector< absl::flat_hash_set< int > > var_to_ub_only_constraints
int index
Definition: pack.cc:509
Domain RelaxIfTooComplex() const
If NumIntervals() is too large, this return a superset of the domain.
ABSL_MUST_USE_RESULT bool IntersectDomainWith(int ref, const Domain &domain, bool *domain_modified=nullptr)
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
Domain MultiplicationBy(int64_t coeff, bool *exact=nullptr) const
Returns {x ∈ Int64, ∃ e ∈ D, x = e * coeff}.
bool VariableWithCostIsUniqueAndRemovable(int ref) const
ABSL_MUST_USE_RESULT bool NotifyThatModelIsUnsat(const std::string &message="")
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
std::vector< int > UsedIntervals(const ConstraintProto &ct)
bool SubstituteVariableInObjective(int var_in_equality, int64_t coeff_in_equality, const ConstraintProto &equality, std::vector< int > *new_vars_in_objective=nullptr)
Domain AdditionWith(const Domain &domain) const
Returns {x ∈ Int64, ∃ a ∈ D, ∃ b ∈ domain, x = a + b}.
bool IsIncludedIn(const Domain &domain) const
Returns true iff D is included in the given domain.
bool DomainContains(int ref, int64_t value) const
#define DCHECK(condition)
Definition: base/logging.h:885
We call domain any subset of Int64 = [kint64min, kint64max].
int Get(PresolveContext *context) const
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
ColIndex representative
bool Contains(int64_t value) const
Returns true iff value is in Domain.
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:886
Domain DomainSuperSetOf(const LinearExpressionProto &expr) const
std::vector< absl::flat_hash_set< int > > var_to_lb_only_constraints
void AddImplyInDomain(int b, int x, const Domain &domain)
int GetOrCreateReifiedPrecedenceLiteral(int time_i, int time_j, int active_i, int active_j)
bool StoreLiteralImpliesVarEqValue(int literal, int var, int64_t value)
Collection of objects used to extend the Constraint Solver library.
::operations_research::sat::CpObjectiveProto * mutable_objective()
bool ExploitExactlyOneInObjective(absl::Span< const int > exactly_one)
const ::operations_research::sat::IntervalConstraintProto & interval() const
std::string IntervalDebugString(int ct_ref) const
::PROTOBUF_NAMESPACE_ID::int32 vars(int index) const
Definition: cp_model.pb.h:6950
bool RefIsPositive(int ref)
constexpr int kObjectiveConstraint
IntVar * var
Definition: expr_array.cc:1874
bool TryAdd(int x, int y, int64_t coeff, int64_t offset)
ABSL_MUST_USE_RESULT bool SetLiteralToTrue(int lit)
#define VLOG_IS_ON(verboselevel)
Definition: vlog_is_on.h:41
int GetOrCreateVarValueEncoding(int ref, int64_t value)
::PROTOBUF_NAMESPACE_ID::int32 vars(int index) const
Definition: cp_model.pb.h:7253
GurobiMPCallbackContext * context
void InsertVarValueEncoding(int literal, int ref, int64_t value)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
bool IsEmpty() const
Returns true if this is the empty set.
int64_t value
Literal literal
Definition: optimization.cc:85
AffineRelation::Relation GetAffineRelation(int ref) const
IntervalVar * interval
Definition: resource.cc:100
#define CHECK_NE(val1, val2)
Definition: base/logging.h:699
const Constraint * ct
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:889
ABSL_MUST_USE_RESULT bool CanonicalizeObjective()
const int INFO
Definition: log_severity.h:31
int64_t a