OR-Tools  8.1
presolve_context.cc
Go to the documentation of this file.
1 // Copyright 2010-2018 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 "ortools/base/map_util.h"
17 #include "ortools/base/mathutil.h"
19 
20 namespace operations_research {
21 namespace sat {
22 
24  return context->GetLiteralRepresentative(ref_);
25 }
26 
28  return context->GetVariableRepresentative(ref_);
29 }
30 
32 
33 int PresolveContext::NewIntVar(const Domain& domain) {
34  IntegerVariableProto* const var = working_model->add_variables();
35  FillDomainInProto(domain, var);
37  return working_model->variables_size() - 1;
38 }
39 
41 
43  if (!gtl::ContainsKey(constant_to_ref_, cst)) {
44  constant_to_ref_[cst] = SavedVariable(working_model->variables_size());
45  IntegerVariableProto* const var_proto = working_model->add_variables();
46  var_proto->add_domain(cst);
47  var_proto->add_domain(cst);
49  }
50  return constant_to_ref_[cst].Get(this);
51 }
52 
53 // a => b.
55  ConstraintProto* const ct = working_model->add_constraints();
56  ct->add_enforcement_literal(a);
57  ct->mutable_bool_and()->add_literals(b);
58 }
59 
60 // b => x in [lb, ub].
61 void PresolveContext::AddImplyInDomain(int b, int x, const Domain& domain) {
62  ConstraintProto* const imply = working_model->add_constraints();
63 
64  // Doing it like this seems to use slightly less memory.
65  // TODO(user): Find the best way to create such small proto.
66  imply->mutable_enforcement_literal()->Resize(1, b);
67  LinearConstraintProto* mutable_linear = imply->mutable_linear();
68  mutable_linear->mutable_vars()->Resize(1, x);
69  mutable_linear->mutable_coeffs()->Resize(1, 1);
70  FillDomainInProto(domain, mutable_linear);
71 }
72 
73 bool PresolveContext::DomainIsEmpty(int ref) const {
74  return domains[PositiveRef(ref)].IsEmpty();
75 }
76 
77 bool PresolveContext::IsFixed(int ref) const {
78  DCHECK_LT(PositiveRef(ref), domains.size());
79  DCHECK(!DomainIsEmpty(ref));
80  return domains[PositiveRef(ref)].IsFixed();
81 }
82 
84  const int var = PositiveRef(ref);
85  return domains[var].Min() >= 0 && domains[var].Max() <= 1;
86 }
87 
88 bool PresolveContext::LiteralIsTrue(int lit) const {
90  if (RefIsPositive(lit)) {
91  return domains[lit].Min() == 1;
92  } else {
93  return domains[PositiveRef(lit)].Max() == 0;
94  }
95 }
96 
97 bool PresolveContext::LiteralIsFalse(int lit) const {
99  if (RefIsPositive(lit)) {
100  return domains[lit].Max() == 0;
101  } else {
102  return domains[PositiveRef(lit)].Min() == 1;
103  }
104 }
105 
107  DCHECK(!DomainIsEmpty(ref));
108  return RefIsPositive(ref) ? domains[PositiveRef(ref)].Min()
109  : -domains[PositiveRef(ref)].Max();
110 }
111 
113  DCHECK(!DomainIsEmpty(ref));
114  return RefIsPositive(ref) ? domains[PositiveRef(ref)].Max()
115  : -domains[PositiveRef(ref)].Min();
116 }
117 
118 int64 PresolveContext::MinOf(const LinearExpressionProto& expr) const {
119  int64 result = expr.offset();
120  for (int i = 0; i < expr.vars_size(); ++i) {
121  const int64 coeff = expr.coeffs(i);
122  if (coeff > 0) {
123  result += coeff * MinOf(expr.vars(i));
124  } else {
125  result += coeff * MaxOf(expr.vars(i));
126  }
127  }
128  return result;
129 }
130 
131 int64 PresolveContext::MaxOf(const LinearExpressionProto& expr) const {
132  int64 result = expr.offset();
133  for (int i = 0; i < expr.vars_size(); ++i) {
134  const int64 coeff = expr.coeffs(i);
135  if (coeff > 0) {
136  result += coeff * MaxOf(expr.vars(i));
137  } else {
138  result += coeff * MinOf(expr.vars(i));
139  }
140  }
141  return result;
142 }
143 
144 // Important: To be sure a variable can be removed, we need it to not be a
145 // representative of both affine and equivalence relation.
146 bool PresolveContext::VariableIsNotRepresentativeOfEquivalenceClass(
147  int var) const {
149  if (affine_relations_.ClassSize(var) > 1 &&
150  affine_relations_.Get(var).representative == var) {
151  return false;
152  }
153  if (var_equiv_relations_.ClassSize(var) > 1 &&
154  var_equiv_relations_.Get(var).representative == var) {
155  return false;
156  }
157  return true;
158 }
159 
160 // Tricky: If this variable is equivalent to another one (but not the
161 // representative) and appear in just one constraint, then this constraint must
162 // be the affine defining one. And in this case the code using this function
163 // should do the proper stuff.
165  if (!ConstraintVariableGraphIsUpToDate()) return false;
166  const int var = PositiveRef(ref);
167  return var_to_constraints_[var].size() == 1 &&
168  VariableIsNotRepresentativeOfEquivalenceClass(var) &&
170 }
171 
172 // Tricky: Same remark as for VariableIsUniqueAndRemovable().
174  if (!ConstraintVariableGraphIsUpToDate()) return false;
175  const int var = PositiveRef(ref);
176  return !keep_all_feasible_solutions &&
177  var_to_constraints_[var].contains(kObjectiveConstraint) &&
178  var_to_constraints_[var].size() == 2 &&
179  VariableIsNotRepresentativeOfEquivalenceClass(var);
180 }
181 
182 // Here, even if the variable is equivalent to others, if its affine defining
183 // constraints where removed, then it is not needed anymore.
185  if (!ConstraintVariableGraphIsUpToDate()) return false;
186  return var_to_constraints_[PositiveRef(ref)].empty();
187 }
188 
190  removed_variables_.insert(PositiveRef(ref));
191 }
192 
193 // Note(user): I added an indirection and a function for this to be able to
194 // display debug information when this return false. This should actually never
195 // return false in the cases where it is used.
197  // It is okay to reuse removed fixed variable.
198  if (IsFixed(ref)) return false;
199  if (!removed_variables_.contains(PositiveRef(ref))) return false;
200  if (!var_to_constraints_[PositiveRef(ref)].empty()) {
201  LOG(INFO) << "Variable " << PositiveRef(ref)
202  << " was removed, yet it appears in some constraints!";
203  LOG(INFO) << "affine relation: "
205  for (const int c : var_to_constraints_[PositiveRef(ref)]) {
206  LOG(INFO) << "constraint #" << c << " : "
207  << (c >= 0 ? working_model->constraints(c).ShortDebugString()
208  : "");
209  }
210  }
211  return true;
212 }
213 
215  if (!ConstraintVariableGraphIsUpToDate()) return false;
216  const int var = PositiveRef(ref);
217  return var_to_num_linear1_[var] == var_to_constraints_[var].size();
218 }
219 
221  Domain result;
222  if (RefIsPositive(ref)) {
223  result = domains[ref];
224  } else {
225  result = domains[PositiveRef(ref)].Negation();
226  }
227  return result;
228 }
229 
231  if (!RefIsPositive(ref)) {
232  return domains[PositiveRef(ref)].Contains(-value);
233  }
234  return domains[ref].Contains(value);
235 }
236 
237 ABSL_MUST_USE_RESULT bool PresolveContext::IntersectDomainWith(
238  int ref, const Domain& domain, bool* domain_modified) {
239  DCHECK(!DomainIsEmpty(ref));
240  const int var = PositiveRef(ref);
241 
242  if (RefIsPositive(ref)) {
243  if (domains[var].IsIncludedIn(domain)) {
244  return true;
245  }
246  domains[var] = domains[var].IntersectionWith(domain);
247  } else {
248  const Domain temp = domain.Negation();
249  if (domains[var].IsIncludedIn(temp)) {
250  return true;
251  }
252  domains[var] = domains[var].IntersectionWith(temp);
253  }
254 
255  if (domain_modified != nullptr) {
256  *domain_modified = true;
257  }
259  if (domains[var].IsEmpty()) {
260  is_unsat = true;
261  return false;
262  }
263 
264  // Propagate the domain of the representative right away.
265  // Note that the recursive call should only by one level deep.
267  if (r.representative == var) return true;
269  DomainOf(var)
270  .AdditionWith(Domain(-r.offset))
272 }
273 
274 ABSL_MUST_USE_RESULT bool PresolveContext::SetLiteralToFalse(int lit) {
275  const int var = PositiveRef(lit);
276  const int64 value = RefIsPositive(lit) ? 0 : 1;
278 }
279 
280 ABSL_MUST_USE_RESULT bool PresolveContext::SetLiteralToTrue(int lit) {
281  return SetLiteralToFalse(NegatedRef(lit));
282 }
283 
284 void PresolveContext::UpdateRuleStats(const std::string& name, int num_times) {
285  if (enable_stats) {
286  VLOG(1) << num_presolve_operations << " : " << name;
287  stats_by_rule_name[name] += num_times;
288  }
289  num_presolve_operations += num_times;
290 }
291 
292 void PresolveContext::UpdateLinear1Usage(const ConstraintProto& ct, int c) {
293  const int old_var = constraint_to_linear1_var_[c];
294  if (old_var >= 0) {
295  var_to_num_linear1_[old_var]--;
296  }
297  if (ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
298  ct.linear().vars().size() == 1) {
299  const int var = PositiveRef(ct.linear().vars(0));
300  constraint_to_linear1_var_[c] = var;
301  var_to_num_linear1_[var]++;
302  }
303 }
304 
305 void PresolveContext::AddVariableUsage(int c) {
306  const ConstraintProto& ct = working_model->constraints(c);
307  constraint_to_vars_[c] = UsedVariables(ct);
308  constraint_to_intervals_[c] = UsedIntervals(ct);
309  for (const int v : constraint_to_vars_[c]) {
311  var_to_constraints_[v].insert(c);
312  }
313  for (const int i : constraint_to_intervals_[c]) interval_usage_[i]++;
314  UpdateLinear1Usage(ct, c);
315 }
316 
318  if (is_unsat) return;
319  DCHECK_EQ(constraint_to_vars_.size(), working_model->constraints_size());
320  const ConstraintProto& ct = working_model->constraints(c);
321 
322  // We don't optimize the interval usage as this is not super frequent.
323  for (const int i : constraint_to_intervals_[c]) interval_usage_[i]--;
324  constraint_to_intervals_[c] = UsedIntervals(ct);
325  for (const int i : constraint_to_intervals_[c]) interval_usage_[i]++;
326 
327  // For the variables, we avoid an erase() followed by an insert() for the
328  // variables that didn't change.
329  tmp_new_usage_ = UsedVariables(ct);
330  const std::vector<int>& old_usage = constraint_to_vars_[c];
331  const int old_size = old_usage.size();
332  int i = 0;
333  for (const int var : tmp_new_usage_) {
335  while (i < old_size && old_usage[i] < var) {
336  var_to_constraints_[old_usage[i]].erase(c);
337  ++i;
338  }
339  if (i < old_size && old_usage[i] == var) {
340  ++i;
341  } else {
342  var_to_constraints_[var].insert(c);
343  }
344  }
345  for (; i < old_size; ++i) var_to_constraints_[old_usage[i]].erase(c);
346  constraint_to_vars_[c] = tmp_new_usage_;
347 
348  UpdateLinear1Usage(ct, c);
349 }
350 
352  return constraint_to_vars_.size() == working_model->constraints_size();
353 }
354 
356  if (is_unsat) return;
357  const int old_size = constraint_to_vars_.size();
358  const int new_size = working_model->constraints_size();
359  CHECK_LE(old_size, new_size);
360  constraint_to_vars_.resize(new_size);
361  constraint_to_linear1_var_.resize(new_size, -1);
362  constraint_to_intervals_.resize(new_size);
363  interval_usage_.resize(new_size);
364  for (int c = old_size; c < new_size; ++c) {
365  AddVariableUsage(c);
366  }
367 }
368 
369 // TODO(user): Also test var_to_constraints_ !!
371  if (is_unsat) return true; // We do not care in this case.
372  if (constraint_to_vars_.size() != working_model->constraints_size()) {
373  LOG(INFO) << "Wrong constraint_to_vars size!";
374  return false;
375  }
376  for (int c = 0; c < constraint_to_vars_.size(); ++c) {
377  if (constraint_to_vars_[c] !=
378  UsedVariables(working_model->constraints(c))) {
379  LOG(INFO) << "Wrong variables usage for constraint: \n"
380  << ProtobufDebugString(working_model->constraints(c))
381  << "old_size: " << constraint_to_vars_[c].size();
382  return false;
383  }
384  }
385  int num_in_objective = 0;
386  for (int v = 0; v < var_to_constraints_.size(); ++v) {
387  if (var_to_constraints_[v].contains(kObjectiveConstraint)) {
388  ++num_in_objective;
389  if (!objective_map.contains(v)) {
390  LOG(INFO) << "Variable " << v
391  << " is marked as part of the objective but isn't.";
392  return false;
393  }
394  }
395  }
396  if (num_in_objective != objective_map.size()) {
397  LOG(INFO) << "Not all variables are marked as part of the objective";
398  return false;
399  }
400 
401  return true;
402 }
403 
404 // If a Boolean variable (one with domain [0, 1]) appear in this affine
405 // equivalence class, then we want its representative to be Boolean. Note that
406 // this is always possible because a Boolean variable can never be equal to a
407 // multiple of another if std::abs(coeff) is greater than 1 and if it is not
408 // fixed to zero. This is important because it allows to simply use the same
409 // representative for any referenced literals.
410 //
411 // Note(user): When both domain contains [0,1] and later the wrong variable
412 // become usable as boolean, then we have a bug. Because of that, the code
413 // for GetLiteralRepresentative() is not as simple as it should be.
414 bool PresolveContext::AddRelation(int x, int y, int64 c, int64 o,
415  AffineRelation* repo) {
416  // When the coefficient is larger than one, then if later one variable becomes
417  // Boolean, it must be the representative.
418  if (std::abs(c) != 1) return repo->TryAdd(x, y, c, o);
419 
422 
423  // To avoid integer overflow, we always want to use the representative with
424  // the smallest domain magnitude. Otherwise we might express a variable in say
425  // [0, 3] as ([x, x + 3] - x) for an arbitrary large x, and substituting
426  // something like this in a linear expression could break our overflow
427  // precondition.
428  //
429  // Note that if either rep_x or rep_y can be used as a literal, then it will
430  // also be the variable with the smallest domain magnitude (1 or 0 if fixed).
431  const int rep_x = repo->Get(x).representative;
432  const int rep_y = repo->Get(y).representative;
433  const int64 m_x = std::max(std::abs(MinOf(rep_x)), std::abs(MaxOf(rep_x)));
434  const int64 m_y = std::max(std::abs(MinOf(rep_y)), std::abs(MaxOf(rep_y)));
435  bool allow_rep_x = m_x < m_y;
436  bool allow_rep_y = m_y < m_x;
437  if (m_x == m_y) {
438  // If both magnitude are the same, we prefer a positive domain.
439  // This is important so we don't use [-1, 0] as a representative for [0, 1].
440  allow_rep_x = MinOf(rep_x) >= MinOf(rep_y);
441  allow_rep_y = MinOf(rep_y) >= MinOf(rep_x);
442  }
443  return repo->TryAdd(x, y, c, o, allow_rep_x, allow_rep_y);
444 }
445 
448  CHECK(IsFixed(var));
449  const int min = MinOf(var);
450  if (gtl::ContainsKey(constant_to_ref_, min)) {
451  const int rep = constant_to_ref_[min].Get(this);
452  if (RefIsPositive(rep)) {
453  if (rep != var) {
454  AddRelation(var, rep, 1, 0, &affine_relations_);
455  AddRelation(var, rep, 1, 0, &var_equiv_relations_);
456  }
457  } else {
458  if (PositiveRef(rep) == var) {
459  CHECK_EQ(min, 0);
460  } else {
461  AddRelation(var, PositiveRef(rep), -1, 0, &affine_relations_);
462  AddRelation(var, PositiveRef(rep), -1, 0, &var_equiv_relations_);
463  }
464  }
465  } else {
466  constant_to_ref_[min] = SavedVariable(var);
467  }
468 }
469 
471  const int var = PositiveRef(ref);
473  if (r.representative == var) return true;
474 
475  // Propagate domains both ways.
476  // var = coeff * rep + offset
478  DomainOf(var)
479  .AdditionWith(Domain(-r.offset))
481  return false;
482  }
485  .AdditionWith(Domain(r.offset)))) {
486  return false;
487  }
488 
489  return true;
490 }
491 
493  for (auto& ref_map : var_to_constraints_) {
494  ref_map.erase(kAffineRelationConstraint);
495  }
496 }
497 
498 // We only call that for a non representative variable that is only used in
499 // the kAffineRelationConstraint. Such variable can be ignored and should never
500 // be seen again in the presolve.
502  const int rep = GetAffineRelation(var).representative;
503 
505  CHECK_NE(var, rep);
506  CHECK_EQ(var_to_constraints_[var].size(), 1);
507  CHECK(var_to_constraints_[var].contains(kAffineRelationConstraint));
508  CHECK(var_to_constraints_[rep].contains(kAffineRelationConstraint));
509 
510  // We shouldn't reuse this variable again!
512 
513  var_to_constraints_[var].erase(kAffineRelationConstraint);
514  affine_relations_.IgnoreFromClassSize(var);
515  var_equiv_relations_.IgnoreFromClassSize(var);
516 
517  // If the representative is left alone, we can remove it from the special
518  // affine relation constraint too.
519  if (affine_relations_.ClassSize(rep) == 1 &&
520  var_equiv_relations_.ClassSize(rep) == 1) {
521  var_to_constraints_[rep].erase(kAffineRelationConstraint);
522  }
523 
524  if (VLOG_IS_ON(2)) {
525  LOG(INFO) << "Removing affine relation: " << AffineRelationDebugString(var);
526  }
527 }
528 
529 bool PresolveContext::StoreAffineRelation(int ref_x, int ref_y, int64 coeff,
530  int64 offset) {
531  CHECK_NE(coeff, 0);
532  if (is_unsat) return false;
533 
534  // TODO(user): I am not 100% sure why, but sometimes the representative is
535  // fixed but that is not propagated to ref_x or ref_y and this causes issues.
536  if (!PropagateAffineRelation(ref_x)) return true;
537  if (!PropagateAffineRelation(ref_y)) return true;
538 
539  if (IsFixed(ref_x)) {
540  const int64 lhs = DomainOf(ref_x).Min() - offset;
541  if (lhs % std::abs(coeff) != 0) {
542  is_unsat = true;
543  return true;
544  }
545  static_cast<void>(IntersectDomainWith(ref_y, Domain(lhs / coeff)));
546  UpdateRuleStats("affine: fixed");
547  return true;
548  }
549 
550  if (IsFixed(ref_y)) {
551  const int64 value_x = DomainOf(ref_y).Min() * coeff + offset;
552  static_cast<void>(IntersectDomainWith(ref_x, Domain(value_x)));
553  UpdateRuleStats("affine: fixed");
554  return true;
555  }
556 
557  // If both are already in the same class, we need to make sure the relations
558  // are compatible.
561  if (rx.representative == ry.representative) {
562  // x = rx.coeff * rep + rx.offset;
563  // y = ry.coeff * rep + ry.offset_y;
564  // And x == coeff * ry.coeff * rep + (coeff * ry.offset + offset).
565  //
566  // So we get the relation a * rep == b with a and b defined here:
567  const int64 a = coeff * ry.coeff - rx.coeff;
568  const int64 b = coeff * ry.offset + offset - rx.offset;
569  if (a == 0) {
570  if (b != 0) is_unsat = true;
571  return true;
572  }
573  if (b % a != 0) {
574  is_unsat = true;
575  return true;
576  }
577  UpdateRuleStats("affine: unique solution");
578  const int64 unique_value = -b / a;
579  if (!IntersectDomainWith(rx.representative, Domain(unique_value))) {
580  return true;
581  }
582  if (!IntersectDomainWith(ref_x,
583  Domain(unique_value * rx.coeff + rx.offset))) {
584  return true;
585  }
586  if (!IntersectDomainWith(ref_y,
587  Domain(unique_value * ry.coeff + ry.offset))) {
588  return true;
589  }
590  return true;
591  }
592 
593  const int x = PositiveRef(ref_x);
594  const int y = PositiveRef(ref_y);
595  const int64 c = RefIsPositive(ref_x) == RefIsPositive(ref_y) ? coeff : -coeff;
596  const int64 o = RefIsPositive(ref_x) ? offset : -offset;
597 
598  // TODO(user): can we force the rep and remove GetAffineRelation()?
599  bool added = AddRelation(x, y, c, o, &affine_relations_);
600  if ((c == 1 || c == -1) && o == 0) {
601  added |= AddRelation(x, y, c, o, &var_equiv_relations_);
602  }
603  if (added) {
604  UpdateRuleStats("affine: new relation");
605 
606  // Lets propagate again the new relation. We might as well do it as early
607  // as possible and not all call site do it.
608  if (!PropagateAffineRelation(ref_x)) return true;
609  if (!PropagateAffineRelation(ref_y)) return true;
610 
611  // These maps should only contains representative, so only need to remap
612  // either x or y.
613  const int rep = GetAffineRelation(x).representative;
614  if (x != rep) encoding_remap_queue_.push_back(x);
615  if (y != rep) encoding_remap_queue_.push_back(y);
616 
617  // The domain didn't change, but this notification allows to re-process any
618  // constraint containing these variables. Note that we do not need to
619  // retrigger a propagation of the constraint containing a variable whose
620  // representative didn't change.
621  if (x != rep) modified_domains.Set(x);
622  if (y != rep) modified_domains.Set(y);
623 
624  var_to_constraints_[x].insert(kAffineRelationConstraint);
625  var_to_constraints_[y].insert(kAffineRelationConstraint);
626  return true;
627  }
628 
629  UpdateRuleStats("affine: incompatible relation");
630  if (VLOG_IS_ON(1)) {
631  LOG(INFO) << "Cannot add relation " << DomainOf(ref_x) << " = " << coeff
632  << " * " << DomainOf(ref_y) << " + " << offset
633  << " because of incompatibilities with existing relation: ";
634  for (const int ref : {ref_x, ref_y}) {
635  const auto r = GetAffineRelation(ref);
636  LOG(INFO) << DomainOf(ref) << " = " << r.coeff << " * "
637  << DomainOf(r.representative) << " + " << r.offset;
638  }
639  }
640 
641  return false;
642 }
643 
645  if (is_unsat) return;
646 
647  CHECK(!VariableWasRemoved(ref_a));
648  CHECK(!VariableWasRemoved(ref_b));
649  CHECK(!DomainOf(ref_a).IsEmpty());
650  CHECK(!DomainOf(ref_b).IsEmpty());
651  CHECK(CanBeUsedAsLiteral(ref_a));
652  CHECK(CanBeUsedAsLiteral(ref_b));
653 
654  if (ref_a == ref_b) return;
655  if (ref_a == NegatedRef(ref_b)) {
656  is_unsat = true;
657  return;
658  }
659  const int var_a = PositiveRef(ref_a);
660  const int var_b = PositiveRef(ref_b);
661  if (RefIsPositive(ref_a) == RefIsPositive(ref_b)) {
662  // a = b
663  CHECK(StoreAffineRelation(var_a, var_b, /*coeff=*/1, /*offset=*/0));
664  } else {
665  // a = 1 - b
666  CHECK(StoreAffineRelation(var_a, var_b, /*coeff=*/-1, /*offset=*/1));
667  }
668 }
669 
670 bool PresolveContext::StoreAbsRelation(int target_ref, int ref) {
671  const auto insert_status = abs_relations_.insert(
672  std::make_pair(target_ref, SavedVariable(PositiveRef(ref))));
673  if (!insert_status.second) {
674  // Tricky: overwrite if the old value refer to a now unused variable.
675  const int candidate = insert_status.first->second.Get(this);
676  if (removed_variables_.contains(candidate)) {
677  insert_status.first->second = SavedVariable(PositiveRef(ref));
678  return true;
679  }
680  return false;
681  }
682  return true;
683 }
684 
685 bool PresolveContext::GetAbsRelation(int target_ref, int* ref) {
686  auto it = abs_relations_.find(target_ref);
687  if (it == abs_relations_.end()) return false;
688 
689  // Tricky: In some rare case the stored relation can refer to a deleted
690  // variable, so we need to ignore it.
691  //
692  // TODO(user): Incorporate this as part of SavedVariable/SavedLiteral so we
693  // make sure we never forget about this.
694  const int candidate = it->second.Get(this);
695  if (removed_variables_.contains(candidate)) {
696  abs_relations_.erase(it);
697  return false;
698  }
699  *ref = candidate;
700  return true;
701 }
702 
705 
708  // Note(user): This can happen is some corner cases where the affine
709  // relation where added before the variable became usable as Boolean. When
710  // this is the case, the domain will be of the form [x, x + 1] and should be
711  // later remapped to a Boolean variable.
712  return ref;
713  }
714 
715  // We made sure that the affine representative can always be used as a
716  // literal. However, if some variable are fixed, we might not have only
717  // (coeff=1 offset=0) or (coeff=-1 offset=1) and we might have something like
718  // (coeff=8 offset=0) which is only valid for both variable at zero...
719  //
720  // What is sure is that depending on the value, only one mapping can be valid
721  // because r.coeff can never be zero.
722  const bool positive_possible = (r.offset == 0 || r.coeff + r.offset == 1);
723  const bool negative_possible = (r.offset == 1 || r.coeff + r.offset == 0);
724  DCHECK_NE(positive_possible, negative_possible);
725  if (RefIsPositive(ref)) {
726  return positive_possible ? r.representative : NegatedRef(r.representative);
727  } else {
728  return positive_possible ? NegatedRef(r.representative) : r.representative;
729  }
730 }
731 
733  const AffineRelation::Relation r = var_equiv_relations_.Get(PositiveRef(ref));
734  CHECK_EQ(std::abs(r.coeff), 1);
735  CHECK_EQ(r.offset, 0);
736  return RefIsPositive(ref) == (r.coeff == 1) ? r.representative
738 }
739 
740 // This makes sure that the affine relation only uses one of the
741 // representative from the var_equiv_relations_.
743  AffineRelation::Relation r = affine_relations_.Get(PositiveRef(ref));
744  AffineRelation::Relation o = var_equiv_relations_.Get(r.representative);
746  if (o.coeff == -1) r.coeff = -r.coeff;
747  if (!RefIsPositive(ref)) {
748  r.coeff *= -1;
749  r.offset *= -1;
750  }
751  return r;
752 }
753 
754 std::string PresolveContext::RefDebugString(int ref) const {
755  return absl::StrCat(RefIsPositive(ref) ? "X" : "-X", PositiveRef(ref),
756  DomainOf(ref).ToString());
757 }
758 
759 std::string PresolveContext::AffineRelationDebugString(int ref) const {
761  return absl::StrCat(RefDebugString(ref), " = ", r.coeff, " * ",
762  RefDebugString(r.representative), " + ", r.offset);
763 }
764 
765 // Create the internal structure for any new variables in working_model.
767  for (int i = domains.size(); i < working_model->variables_size(); ++i) {
768  domains.emplace_back(ReadDomainFromProto(working_model->variables(i)));
769  if (domains.back().IsEmpty()) {
770  is_unsat = true;
771  return;
772  }
773  if (IsFixed(i)) ExploitFixedDomain(i);
774  }
775  modified_domains.Resize(domains.size());
776  var_to_constraints_.resize(domains.size());
777  var_to_num_linear1_.resize(domains.size());
778  var_to_ub_only_constraints.resize(domains.size());
779  var_to_lb_only_constraints.resize(domains.size());
780 }
781 
782 bool PresolveContext::RemapEncodingMaps() {
783  // TODO(user): for now, while the code works most of the time, it triggers
784  // weird side effect that causes some issues in some LNS presolve...
785  // We should continue the investigation before activating it.
786  //
787  // Note also that because all our encoding constraints are present in the
788  // model, they will be remapped, and the new mapping re-added again. So while
789  // the current code might not be efficient, it should eventually reach the
790  // same effect.
791  encoding_remap_queue_.clear();
792 
793  // Note that InsertVarValueEncodingInternal() will potentially add new entry
794  // to the encoding_ map, but for a different variables. So this code relies on
795  // the fact that the var_map shouldn't change content nor address of the
796  // "var_map" below while we iterate on them.
797  for (const int var : encoding_remap_queue_) {
800  if (r.representative == var) return true;
801  int num_remapping = 0;
802 
803  // Encoding.
804  {
805  const absl::flat_hash_map<int64, SavedLiteral>& var_map = encoding_[var];
806  for (const auto& entry : var_map) {
807  const int lit = entry.second.Get(this);
808  if (removed_variables_.contains(PositiveRef(lit))) continue;
809  if ((entry.first - r.offset) % r.coeff != 0) continue;
810  const int64 rep_value = (entry.first - r.offset) / r.coeff;
811  ++num_remapping;
812  InsertVarValueEncodingInternal(lit, r.representative, rep_value,
813  /*add_constraints=*/false);
814  if (is_unsat) return false;
815  }
816  encoding_.erase(var);
817  }
818 
819  // Eq half encoding.
820  {
821  const absl::flat_hash_map<int64, absl::flat_hash_set<int>>& var_map =
822  eq_half_encoding_[var];
823  for (const auto& entry : var_map) {
824  if ((entry.first - r.offset) % r.coeff != 0) continue;
825  const int64 rep_value = (entry.first - r.offset) / r.coeff;
826  for (int literal : entry.second) {
827  ++num_remapping;
828  InsertHalfVarValueEncoding(GetLiteralRepresentative(literal),
829  r.representative, rep_value,
830  /*imply_eq=*/true);
831  if (is_unsat) return false;
832  }
833  }
834  eq_half_encoding_.erase(var);
835  }
836 
837  // Neq half encoding.
838  {
839  const absl::flat_hash_map<int64, absl::flat_hash_set<int>>& var_map =
840  neq_half_encoding_[var];
841  for (const auto& entry : var_map) {
842  if ((entry.first - r.offset) % r.coeff != 0) continue;
843  const int64 rep_value = (entry.first - r.offset) / r.coeff;
844  for (int literal : entry.second) {
845  ++num_remapping;
846  InsertHalfVarValueEncoding(GetLiteralRepresentative(literal),
847  r.representative, rep_value,
848  /*imply_eq=*/false);
849  if (is_unsat) return false;
850  }
851  }
852  neq_half_encoding_.erase(var);
853  }
854 
855  if (num_remapping > 0) {
856  VLOG(1) << "Remapped " << num_remapping << " encodings due to " << var
857  << " -> " << r.representative << ".";
858  }
859  }
860  encoding_remap_queue_.clear();
861  return !is_unsat;
862 }
863 
866  CHECK_EQ(DomainOf(var).Size(), 2);
867  const int64 var_min = MinOf(var);
868  const int64 var_max = MaxOf(var);
869 
870  if (is_unsat) return;
871 
872  absl::flat_hash_map<int64, SavedLiteral>& var_map = encoding_[var];
873 
874  // Find encoding for min if present.
875  auto min_it = var_map.find(var_min);
876  if (min_it != var_map.end()) {
877  const int old_var = PositiveRef(min_it->second.Get(this));
878  if (removed_variables_.contains(old_var)) {
879  var_map.erase(min_it);
880  min_it = var_map.end();
881  }
882  }
883 
884  // Find encoding for max if present.
885  auto max_it = var_map.find(var_max);
886  if (max_it != var_map.end()) {
887  const int old_var = PositiveRef(max_it->second.Get(this));
888  if (removed_variables_.contains(old_var)) {
889  var_map.erase(max_it);
890  max_it = var_map.end();
891  }
892  }
893 
894  // Insert missing encoding.
895  int min_literal;
896  int max_literal;
897  if (min_it != var_map.end() && max_it != var_map.end()) {
898  min_literal = min_it->second.Get(this);
899  max_literal = max_it->second.Get(this);
900  if (min_literal != NegatedRef(max_literal)) {
901  UpdateRuleStats("variables with 2 values: merge encoding literals");
902  StoreBooleanEqualityRelation(min_literal, NegatedRef(max_literal));
903  if (is_unsat) return;
904  }
905  min_literal = GetLiteralRepresentative(min_literal);
906  max_literal = GetLiteralRepresentative(max_literal);
907  if (!IsFixed(min_literal)) CHECK_EQ(min_literal, NegatedRef(max_literal));
908  } else if (min_it != var_map.end() && max_it == var_map.end()) {
909  UpdateRuleStats("variables with 2 values: register other encoding");
910  min_literal = min_it->second.Get(this);
911  max_literal = NegatedRef(min_literal);
912  var_map[var_max] = SavedLiteral(max_literal);
913  } else if (min_it == var_map.end() && max_it != var_map.end()) {
914  UpdateRuleStats("variables with 2 values: register other encoding");
915  max_literal = max_it->second.Get(this);
916  min_literal = NegatedRef(max_literal);
917  var_map[var_min] = SavedLiteral(min_literal);
918  } else {
919  UpdateRuleStats("variables with 2 values: create encoding literal");
920  max_literal = NewBoolVar();
921  min_literal = NegatedRef(max_literal);
922  var_map[var_min] = SavedLiteral(min_literal);
923  var_map[var_max] = SavedLiteral(max_literal);
924  }
925 
926  if (IsFixed(min_literal) || IsFixed(max_literal)) {
927  CHECK(IsFixed(min_literal));
928  CHECK(IsFixed(max_literal));
929  UpdateRuleStats("variables with 2 values: fixed encoding");
930  if (LiteralIsTrue(min_literal)) {
931  return static_cast<void>(IntersectDomainWith(var, Domain(var_min)));
932  } else {
933  return static_cast<void>(IntersectDomainWith(var, Domain(var_max)));
934  }
935  }
936 
937  // Add affine relation.
938  if (GetAffineRelation(var).representative != PositiveRef(min_literal)) {
939  UpdateRuleStats("variables with 2 values: new affine relation");
940  if (RefIsPositive(max_literal)) {
942  var_max - var_min, var_min));
943  } else {
945  var_min - var_max, var_max));
946  }
947  }
948 }
949 
950 void PresolveContext::InsertVarValueEncodingInternal(int literal, int var,
951  int64 value,
952  bool add_constraints) {
955  absl::flat_hash_map<int64, SavedLiteral>& var_map = encoding_[var];
956 
957  // Ticky and rare: I have only observed this on the LNS of
958  // radiation_m18_12_05_sat.fzn. The value was encoded, but maybe we never
959  // used the involved variables / constraints, so it was removed (with the
960  // encoding constraints) from the model already! We have to be careful.
961  const auto it = var_map.find(value);
962  if (it != var_map.end()) {
963  const int old_var = PositiveRef(it->second.Get(this));
964  if (removed_variables_.contains(old_var)) {
965  var_map.erase(it);
966  }
967  }
968 
969  const auto insert =
970  var_map.insert(std::make_pair(value, SavedLiteral(literal)));
971 
972  // If an encoding already exist, make the two Boolean equals.
973  if (!insert.second) {
974  const int previous_literal = insert.first->second.Get(this);
975  CHECK(!VariableWasRemoved(previous_literal));
976  if (literal != previous_literal) {
978  "variables: merge equivalent var value encoding literals");
979  StoreBooleanEqualityRelation(literal, previous_literal);
980  }
981  return;
982  }
983 
984  if (DomainOf(var).Size() == 2) {
986  } else {
987  VLOG(2) << "Insert lit(" << literal << ") <=> var(" << var
988  << ") == " << value;
989  eq_half_encoding_[var][value].insert(literal);
990  neq_half_encoding_[var][value].insert(NegatedRef(literal));
991  if (add_constraints) {
992  UpdateRuleStats("variables: add encoding constraint");
993  AddImplyInDomain(literal, var, Domain(value));
994  AddImplyInDomain(NegatedRef(literal), var, Domain(value).Complement());
995  }
996  }
997 }
998 
999 bool PresolveContext::InsertHalfVarValueEncoding(int literal, int var,
1000  int64 value, bool imply_eq) {
1001  if (is_unsat) return false;
1003 
1004  // Creates the linking sets on demand.
1005  // Insert the enforcement literal in the half encoding map.
1006  auto& direct_set =
1007  imply_eq ? eq_half_encoding_[var][value] : neq_half_encoding_[var][value];
1008  if (!direct_set.insert(literal).second) return false; // Already there.
1009 
1010  VLOG(2) << "Collect lit(" << literal << ") implies var(" << var
1011  << (imply_eq ? ") == " : ") != ") << value;
1012  UpdateRuleStats("variables: detect half reified value encoding");
1013 
1014  // Note(user): We don't expect a lot of literals in these sets, so doing
1015  // a scan should be okay.
1016  auto& other_set =
1017  imply_eq ? neq_half_encoding_[var][value] : eq_half_encoding_[var][value];
1018  for (const int other : other_set) {
1019  if (GetLiteralRepresentative(other) != NegatedRef(literal)) continue;
1020 
1021  UpdateRuleStats("variables: detect fully reified value encoding");
1022  const int imply_eq_literal = imply_eq ? literal : NegatedRef(literal);
1023  InsertVarValueEncodingInternal(imply_eq_literal, var, value,
1024  /*add_constraints=*/false);
1025  break;
1026  }
1027 
1028  return true;
1029 }
1030 
1031 bool PresolveContext::CanonicalizeEncoding(int* ref, int64* value) {
1032  const AffineRelation::Relation r = GetAffineRelation(*ref);
1033  if ((*value - r.offset) % r.coeff != 0) return false;
1034  *ref = r.representative;
1035  *value = (*value - r.offset) / r.coeff;
1036  return true;
1037 }
1038 
1040  int64 value) {
1041  if (!RemapEncodingMaps()) return;
1042  if (!CanonicalizeEncoding(&ref, &value)) return;
1044  InsertVarValueEncodingInternal(literal, ref, value, /*add_constraints=*/true);
1045 }
1046 
1048  int64 value) {
1049  if (!RemapEncodingMaps()) return false;
1050  if (!CanonicalizeEncoding(&var, &value)) return false;
1052  return InsertHalfVarValueEncoding(literal, var, value, /*imply_eq=*/true);
1053 }
1054 
1056  int64 value) {
1057  if (!RemapEncodingMaps()) return false;
1058  if (!CanonicalizeEncoding(&var, &value)) return false;
1060  return InsertHalfVarValueEncoding(literal, var, value, /*imply_eq=*/false);
1061 }
1062 
1064  if (!RemapEncodingMaps()) return false;
1065  if (!CanonicalizeEncoding(&ref, &value)) return false;
1066  const absl::flat_hash_map<int64, SavedLiteral>& var_map = encoding_[ref];
1067  const auto it = var_map.find(value);
1068  if (it != var_map.end()) {
1069  if (literal != nullptr) {
1070  *literal = it->second.Get(this);
1071  }
1072  return true;
1073  }
1074  return false;
1075 }
1076 
1078  if (!RemapEncodingMaps()) return GetOrCreateConstantVar(0);
1079  if (!CanonicalizeEncoding(&ref, &value)) return GetOrCreateConstantVar(0);
1080 
1081  // Positive after CanonicalizeEncoding().
1082  const int var = ref;
1083 
1084  // Returns the false literal if the value is not in the domain.
1085  if (!domains[var].Contains(value)) {
1086  return GetOrCreateConstantVar(0);
1087  }
1088 
1089  // Returns the associated literal if already present.
1090  absl::flat_hash_map<int64, SavedLiteral>& var_map = encoding_[var];
1091  auto it = var_map.find(value);
1092  if (it != var_map.end()) {
1093  return it->second.Get(this);
1094  }
1095 
1096  // Special case for fixed domains.
1097  if (domains[var].Size() == 1) {
1098  const int true_literal = GetOrCreateConstantVar(1);
1099  var_map[value] = SavedLiteral(true_literal);
1100  return true_literal;
1101  }
1102 
1103  // Special case for domains of size 2.
1104  const int64 var_min = MinOf(var);
1105  const int64 var_max = MaxOf(var);
1106  if (domains[var].Size() == 2) {
1107  // Checks if the other value is already encoded.
1108  const int64 other_value = value == var_min ? var_max : var_min;
1109  auto other_it = var_map.find(other_value);
1110  if (other_it != var_map.end()) {
1111  // Update the encoding map. The domain could have been reduced to size
1112  // two after the creation of the first literal.
1113  const int literal = NegatedRef(other_it->second.Get(this));
1114  var_map[value] = SavedLiteral(literal);
1115  return literal;
1116  }
1117 
1118  if (var_min == 0 && var_max == 1) {
1120  var_map[1] = SavedLiteral(representative);
1121  var_map[0] = SavedLiteral(NegatedRef(representative));
1122  return value == 1 ? representative : NegatedRef(representative);
1123  } else {
1124  const int literal = NewBoolVar();
1125  InsertVarValueEncoding(literal, var, var_max);
1127  return value == var_max ? representative : NegatedRef(representative);
1128  }
1129  }
1130 
1131  const int literal = NewBoolVar();
1134 }
1135 
1137  const CpObjectiveProto& obj = working_model->objective();
1138 
1139  objective_offset = obj.offset();
1140  objective_scaling_factor = obj.scaling_factor();
1141  if (objective_scaling_factor == 0.0) {
1142  objective_scaling_factor = 1.0;
1143  }
1144  if (!obj.domain().empty()) {
1145  // We might relax this in CanonicalizeObjective() when we will compute
1146  // the possible objective domain from the domains of the variables.
1147  objective_domain_is_constraining = true;
1148  objective_domain = ReadDomainFromProto(obj);
1149  } else {
1150  objective_domain_is_constraining = false;
1151  objective_domain = Domain::AllValues();
1152  }
1153 
1154  objective_map.clear();
1155  for (int i = 0; i < obj.vars_size(); ++i) {
1156  const int ref = obj.vars(i);
1157  int64 coeff = obj.coeffs(i);
1158  if (!RefIsPositive(ref)) coeff = -coeff;
1159  int var = PositiveRef(ref);
1160 
1161  objective_map[var] += coeff;
1162  if (objective_map[var] == 0) {
1163  objective_map.erase(var);
1164  var_to_constraints_[var].erase(kObjectiveConstraint);
1165  } else {
1166  var_to_constraints_[var].insert(kObjectiveConstraint);
1167  }
1168  }
1169 }
1170 
1172  int64 offset_change = 0;
1173 
1174  // We replace each entry by its affine representative.
1175  // Note that the non-deterministic loop is fine, but because we iterate
1176  // one the map while modifying it, it is safer to do a copy rather than to
1177  // try to handle that in one pass.
1178  tmp_entries.clear();
1179  for (const auto& entry : objective_map) {
1180  tmp_entries.push_back(entry);
1181  }
1182 
1183  // TODO(user): This is a bit duplicated with the presolve linear code.
1184  // We also do not propagate back any domain restriction from the objective to
1185  // the variables if any.
1186  for (const auto& entry : tmp_entries) {
1187  const int var = entry.first;
1188  const auto it = objective_map.find(var);
1189  if (it == objective_map.end()) continue;
1190  const int64 coeff = it->second;
1191 
1192  // If a variable only appear in objective, we can fix it!
1193  // Note that we don't care if it was in affine relation, because if none
1194  // of the relations are left, then we can still fix it.
1195  if (!keep_all_feasible_solutions && !objective_domain_is_constraining &&
1197  var_to_constraints_[var].size() == 1 &&
1198  var_to_constraints_[var].contains(kObjectiveConstraint)) {
1199  UpdateRuleStats("objective: variable not used elsewhere");
1200  if (coeff > 0) {
1201  if (!IntersectDomainWith(var, Domain(MinOf(var)))) {
1202  return false;
1203  }
1204  } else {
1205  if (!IntersectDomainWith(var, Domain(MaxOf(var)))) {
1206  return false;
1207  }
1208  }
1209  }
1210 
1211  if (IsFixed(var)) {
1212  offset_change += coeff * MinOf(var);
1213  var_to_constraints_[var].erase(kObjectiveConstraint);
1214  objective_map.erase(var);
1215  continue;
1216  }
1217 
1219  if (r.representative == var) continue;
1220 
1221  objective_map.erase(var);
1222  var_to_constraints_[var].erase(kObjectiveConstraint);
1223 
1224  // Do the substitution.
1225  offset_change += coeff * r.offset;
1226  const int64 new_coeff = objective_map[r.representative] += coeff * r.coeff;
1227 
1228  // Process new term.
1229  if (new_coeff == 0) {
1230  objective_map.erase(r.representative);
1231  var_to_constraints_[r.representative].erase(kObjectiveConstraint);
1232  } else {
1233  var_to_constraints_[r.representative].insert(kObjectiveConstraint);
1234  if (IsFixed(r.representative)) {
1235  offset_change += new_coeff * MinOf(r.representative);
1236  var_to_constraints_[r.representative].erase(kObjectiveConstraint);
1237  objective_map.erase(r.representative);
1238  }
1239  }
1240  }
1241 
1242  Domain implied_domain(0);
1243  int64 gcd(0);
1244 
1245  // We need to sort the entries to be deterministic.
1246  tmp_entries.clear();
1247  for (const auto& entry : objective_map) {
1248  tmp_entries.push_back(entry);
1249  }
1250  std::sort(tmp_entries.begin(), tmp_entries.end());
1251  for (const auto& entry : tmp_entries) {
1252  const int var = entry.first;
1253  const int64 coeff = entry.second;
1254  gcd = MathUtil::GCD64(gcd, std::abs(coeff));
1255  implied_domain =
1256  implied_domain.AdditionWith(DomainOf(var).MultiplicationBy(coeff))
1257  .RelaxIfTooComplex();
1258  }
1259 
1260  // This is the new domain.
1261  // Note that the domain never include the offset.
1262  objective_domain = objective_domain.AdditionWith(Domain(-offset_change))
1263  .IntersectionWith(implied_domain);
1264  objective_domain =
1265  objective_domain.SimplifyUsingImpliedDomain(implied_domain);
1266 
1267  // Updat the offset.
1268  objective_offset += offset_change;
1269 
1270  // Maybe divide by GCD.
1271  if (gcd > 1) {
1272  for (auto& entry : objective_map) {
1273  entry.second /= gcd;
1274  }
1275  objective_domain = objective_domain.InverseMultiplicationBy(gcd);
1276  objective_offset /= static_cast<double>(gcd);
1277  objective_scaling_factor *= static_cast<double>(gcd);
1278  }
1279 
1280  if (objective_domain.IsEmpty()) return false;
1281 
1282  // Detect if the objective domain do not limit the "optimal" objective value.
1283  // If this is true, then we can apply any reduction that reduce the objective
1284  // value without any issues.
1285  objective_domain_is_constraining =
1286  !implied_domain
1287  .IntersectionWith(Domain(kint64min, objective_domain.Max()))
1288  .IsIncludedIn(objective_domain);
1289  return true;
1290 }
1291 
1293  int var_in_equality, int64 coeff_in_equality,
1294  const ConstraintProto& equality, std::vector<int>* new_vars_in_objective) {
1295  CHECK(equality.enforcement_literal().empty());
1296  CHECK(RefIsPositive(var_in_equality));
1297 
1298  if (new_vars_in_objective != nullptr) new_vars_in_objective->clear();
1299 
1300  // We can only "easily" substitute if the objective coefficient is a multiple
1301  // of the one in the constraint.
1302  const int64 coeff_in_objective =
1303  gtl::FindOrDie(objective_map, var_in_equality);
1304  CHECK_NE(coeff_in_equality, 0);
1305  CHECK_EQ(coeff_in_objective % coeff_in_equality, 0);
1306  const int64 multiplier = coeff_in_objective / coeff_in_equality;
1307 
1308  for (int i = 0; i < equality.linear().vars().size(); ++i) {
1309  int var = equality.linear().vars(i);
1310  int64 coeff = equality.linear().coeffs(i);
1311  if (!RefIsPositive(var)) {
1312  var = NegatedRef(var);
1313  coeff = -coeff;
1314  }
1315  if (var == var_in_equality) continue;
1316 
1317  int64& map_ref = objective_map[var];
1318  if (map_ref == 0 && new_vars_in_objective != nullptr) {
1319  new_vars_in_objective->push_back(var);
1320  }
1321  map_ref -= coeff * multiplier;
1322 
1323  if (map_ref == 0) {
1324  objective_map.erase(var);
1325  var_to_constraints_[var].erase(kObjectiveConstraint);
1326  } else {
1327  var_to_constraints_[var].insert(kObjectiveConstraint);
1328  }
1329  }
1330 
1331  objective_map.erase(var_in_equality);
1332  var_to_constraints_[var_in_equality].erase(kObjectiveConstraint);
1333 
1334  // Deal with the offset.
1335  Domain offset = ReadDomainFromProto(equality.linear());
1336  DCHECK_EQ(offset.Min(), offset.Max());
1337  bool exact = true;
1338  offset = offset.MultiplicationBy(multiplier, &exact);
1339  CHECK(exact);
1340 
1341  // Tricky: The objective domain is without the offset, so we need to shift it.
1342  objective_offset += static_cast<double>(offset.Min());
1343  objective_domain = objective_domain.AdditionWith(Domain(-offset.Min()));
1344 
1345  // Because we can assume that the constraint we used was constraining
1346  // (otherwise it would have been removed), the objective domain should be now
1347  // constraining.
1348  objective_domain_is_constraining = true;
1349 
1350  if (objective_domain.IsEmpty()) {
1351  return (void)NotifyThatModelIsUnsat();
1352  }
1353 }
1354 
1356  // We need to sort the entries to be deterministic.
1357  std::vector<std::pair<int, int64>> entries;
1358  for (const auto& entry : objective_map) {
1359  entries.push_back(entry);
1360  }
1361  std::sort(entries.begin(), entries.end());
1362 
1363  CpObjectiveProto* mutable_obj = working_model->mutable_objective();
1364  mutable_obj->set_offset(objective_offset);
1365  mutable_obj->set_scaling_factor(objective_scaling_factor);
1366  FillDomainInProto(objective_domain, mutable_obj);
1367  mutable_obj->clear_vars();
1368  mutable_obj->clear_coeffs();
1369  for (const auto& entry : entries) {
1370  mutable_obj->add_vars(entry.first);
1371  mutable_obj->add_coeffs(entry.second);
1372  }
1373 }
1374 
1375 } // namespace sat
1376 } // namespace operations_research
operations_research::AffineRelation::Relation::coeff
int64 coeff
Definition: affine_relation.h:78
var
IntVar * var
Definition: expr_array.cc:1858
operations_research::sat::PresolveContext::AddImplication
void AddImplication(int a, int b)
Definition: presolve_context.cc:54
INFO
const int INFO
Definition: log_severity.h:31
operations_research::sat::PresolveContext::StoreAffineRelation
bool StoreAffineRelation(int ref_x, int ref_y, int64 coeff, int64 offset)
Definition: presolve_context.cc:529
min
int64 min
Definition: alldiff_cst.cc:138
DCHECK_LT
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:888
map_util.h
VLOG
#define VLOG(verboselevel)
Definition: base/logging.h:978
max
int64 max
Definition: alldiff_cst.cc:139
operations_research::sat::PresolveContext::StoreLiteralImpliesVarEqValue
bool StoreLiteralImpliesVarEqValue(int literal, int var, int64 value)
Definition: presolve_context.cc:1047
operations_research::sat::PresolveContext::modified_domains
SparseBitset< int64 > modified_domains
Definition: presolve_context.h:379
operations_research::AffineRelation::Get
Relation Get(int x) const
Definition: affine_relation.h:210
operations_research::sat::PresolveContext::AddImplyInDomain
void AddImplyInDomain(int b, int x, const Domain &domain)
Definition: presolve_context.cc:61
LOG
#define LOG(severity)
Definition: base/logging.h:420
operations_research::sat::PresolveContext::DomainOf
Domain DomainOf(int ref) const
Definition: presolve_context.cc:220
operations_research::sat::PresolveContext::UpdateConstraintVariableUsage
void UpdateConstraintVariableUsage(int c)
Definition: presolve_context.cc:317
operations_research::sat::PresolveContext::VariableWithCostIsUniqueAndRemovable
bool VariableWithCostIsUniqueAndRemovable(int ref) const
Definition: presolve_context.cc:173
operations_research::sat::PresolveContext::ReadObjectiveFromProto
void ReadObjectiveFromProto()
Definition: presolve_context.cc:1136
operations_research::sat::PresolveContext::SubstituteVariableInObjective
void SubstituteVariableInObjective(int var_in_equality, int64 coeff_in_equality, const ConstraintProto &equality, std::vector< int > *new_vars_in_objective=nullptr)
Definition: presolve_context.cc:1292
proto_utils.h
operations_research::sat::PresolveContext::MaxOf
int64 MaxOf(int ref) const
Definition: presolve_context.cc:112
presolve_context.h
operations_research::sat::UsedVariables
std::vector< int > UsedVariables(const ConstraintProto &ct)
Definition: cp_model_utils.cc:429
operations_research::Domain::IsEmpty
bool IsEmpty() const
Returns true if this is the empty set.
Definition: sorted_interval_list.cc:190
operations_research::AffineRelation::ClassSize
int ClassSize(int x) const
Definition: affine_relation.h:107
operations_research::sat::PresolveContext::NewIntVar
int NewIntVar(const Domain &domain)
Definition: presolve_context.cc:33
value
int64 value
Definition: demon_profiler.cc:43
operations_research::AffineRelation::TryAdd
bool TryAdd(int x, int y, int64 coeff, int64 offset)
Definition: affine_relation.h:164
operations_research
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
Definition: dense_doubly_linked_list.h:21
operations_research::MathUtil::GCD64
static int64 GCD64(int64 x, int64 y)
Definition: mathutil.h:107
operations_research::sat::PresolveContext::PropagateAffineRelation
bool PropagateAffineRelation(int ref)
Definition: presolve_context.cc:470
operations_research::sat::PresolveContext::LiteralIsTrue
bool LiteralIsTrue(int lit) const
Definition: presolve_context.cc:88
operations_research::Domain::AdditionWith
Domain AdditionWith(const Domain &domain) const
Returns {x ∈ Int64, ∃ a ∈ D, ∃ b ∈ domain, x = a + b}.
Definition: sorted_interval_list.cc:332
kint64min
static const int64 kint64min
Definition: integral_types.h:60
operations_research::sat::PresolveContext::IsFixed
bool IsFixed(int ref) const
Definition: presolve_context.cc:77
operations_research::sat::PresolveContext::VariableWasRemoved
bool VariableWasRemoved(int ref) const
Definition: presolve_context.cc:196
operations_research::sat::PresolveContext::DomainIsEmpty
bool DomainIsEmpty(int ref) const
Definition: presolve_context.cc:73
operations_research::sat::PresolveContext::IntersectDomainWith
ABSL_MUST_USE_RESULT bool IntersectDomainWith(int ref, const Domain &domain, bool *domain_modified=nullptr)
Definition: presolve_context.cc:237
operations_research::Domain
We call domain any subset of Int64 = [kint64min, kint64max].
Definition: sorted_interval_list.h:81
int64
int64_t int64
Definition: integral_types.h:34
operations_research::sat::PresolveContext::GetLiteralRepresentative
int GetLiteralRepresentative(int ref) const
Definition: presolve_context.cc:703
operations_research::ToString
const absl::string_view ToString(MPSolver::OptimizationProblemType optimization_problem_type)
Definition: linear_solver.cc:569
operations_research::sat::SavedLiteral::Get
int Get(PresolveContext *context) const
Definition: presolve_context.cc:23
operations_research::sat::PresolveContext::GetOrCreateConstantVar
int GetOrCreateConstantVar(int64 cst)
Definition: presolve_context.cc:42
operations_research::Domain::IsIncludedIn
bool IsIncludedIn(const Domain &domain) const
Returns true iff D is included in the given domain.
Definition: sorted_interval_list.cc:232
operations_research::Domain::IntersectionWith
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
Definition: sorted_interval_list.cc:282
operations_research::sat::PresolveContext::var_to_lb_only_constraints
std::vector< absl::flat_hash_set< int > > var_to_lb_only_constraints
Definition: presolve_context.h:346
operations_research::sat::PresolveContext::GetAffineRelation
AffineRelation::Relation GetAffineRelation(int ref) const
Definition: presolve_context.cc:742
context
GurobiMPCallbackContext * context
Definition: gurobi_interface.cc:509
operations_research::sat::PresolveContext::keep_all_feasible_solutions
bool keep_all_feasible_solutions
Definition: presolve_context.h:356
operations_research::sat::PresolveContext::RemoveVariableFromAffineRelation
void RemoveVariableFromAffineRelation(int var)
Definition: presolve_context.cc:501
operations_research::sat::PresolveContext::DomainContains
bool DomainContains(int ref, int64 value) const
Definition: presolve_context.cc:230
operations_research::Domain::InverseMultiplicationBy
Domain InverseMultiplicationBy(const int64 coeff) const
Returns {x ∈ Int64, ∃ e ∈ D, x * coeff = e}.
Definition: sorted_interval_list.cc:441
operations_research::sat::PresolveContext::VariableIsUniqueAndRemovable
bool VariableIsUniqueAndRemovable(int ref) const
Definition: presolve_context.cc:164
gtl::FindOrDie
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:176
operations_research::sat::UsedIntervals
std::vector< int > UsedIntervals(const ConstraintProto &ct)
Definition: cp_model_utils.cc:444
operations_research::SparseBitset::Set
void Set(IntegerType index)
Definition: bitset.h:805
operations_research::ProtobufDebugString
std::string ProtobufDebugString(const P &message)
Definition: port/proto_utils.h:53
DCHECK_NE
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:886
operations_research::sat::PositiveRef
int PositiveRef(int ref)
Definition: cp_model_utils.h:33
operations_research::sat::PresolveContext::ExploitFixedDomain
void ExploitFixedDomain(int var)
Definition: presolve_context.cc:446
a
int64 a
Definition: constraint_solver/table.cc:42
operations_research::sat::PresolveContext::StoreBooleanEqualityRelation
void StoreBooleanEqualityRelation(int ref_a, int ref_b)
Definition: presolve_context.cc:644
operations_research::sat::SavedVariable
Definition: presolve_context.h:60
operations_research::sat::PresolveContext::MarkVariableAsRemoved
void MarkVariableAsRemoved(int ref)
Definition: presolve_context.cc:189
operations_research::sat::PresolveContext::stats_by_rule_name
absl::flat_hash_map< std::string, int > stats_by_rule_name
Definition: presolve_context.h:363
operations_research::sat::PresolveContext::GetOrCreateVarValueEncoding
int GetOrCreateVarValueEncoding(int ref, int64 value)
Definition: presolve_context.cc:1077
operations_research::sat::PresolveContext::LiteralIsFalse
bool LiteralIsFalse(int lit) const
Definition: presolve_context.cc:97
operations_research::sat::PresolveContext::num_presolve_operations
int64 num_presolve_operations
Definition: presolve_context.h:370
operations_research::AffineRelation::Relation
Definition: affine_relation.h:76
operations_research::sat::PresolveContext::RemoveAllVariablesFromAffineRelationConstraint
void RemoveAllVariablesFromAffineRelationConstraint()
Definition: presolve_context.cc:492
mathutil.h
CHECK_EQ
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:697
representative
ColIndex representative
Definition: preprocessor.cc:424
operations_research::sat::PresolveContext::InitializeNewDomains
void InitializeNewDomains()
Definition: presolve_context.cc:766
ct
const Constraint * ct
Definition: demon_profiler.cc:42
operations_research::sat::PresolveContext::MinOf
int64 MinOf(int ref) const
Definition: presolve_context.cc:106
DCHECK
#define DCHECK(condition)
Definition: base/logging.h:884
operations_research::sat::NegatedRef
int NegatedRef(int ref)
Definition: cp_model_utils.h:32
operations_research::sat::PresolveContext::GetVariableRepresentative
int GetVariableRepresentative(int ref) const
Definition: presolve_context.cc:732
CHECK_LE
#define CHECK_LE(val1, val2)
Definition: base/logging.h:699
operations_research::sat::PresolveContext::UpdateNewConstraintsVariableUsage
void UpdateNewConstraintsVariableUsage()
Definition: presolve_context.cc:355
operations_research::Domain::Min
int64 Min() const
Returns the min value of the domain.
Definition: sorted_interval_list.cc:206
operations_research::AffineRelation::Relation::offset
int64 offset
Definition: affine_relation.h:79
operations_research::sat::PresolveContext::WriteObjectiveToProto
void WriteObjectiveToProto() const
Definition: presolve_context.cc:1355
operations_research::sat::SavedLiteral
Definition: presolve_context.h:49
operations_research::sat::PresolveContext::StoreAbsRelation
bool StoreAbsRelation(int target_ref, int ref)
Definition: presolve_context.cc:670
operations_research::sat::PresolveContext
Definition: presolve_context.h:72
operations_research::sat::PresolveContext::CanonicalizeObjective
ABSL_MUST_USE_RESULT bool CanonicalizeObjective()
Definition: presolve_context.cc:1171
operations_research::sat::PresolveContext::NotifyThatModelIsUnsat
ABSL_MUST_USE_RESULT bool NotifyThatModelIsUnsat(const std::string &message="")
Definition: presolve_context.h:142
operations_research::sat::PresolveContext::ConstraintVariableUsageIsConsistent
bool ConstraintVariableUsageIsConsistent()
Definition: presolve_context.cc:370
operations_research::SparseBitset::Resize
void Resize(IntegerType size)
Definition: bitset.h:791
operations_research::sat::FillDomainInProto
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
Definition: cp_model_utils.h:91
operations_research::sat::RefIsPositive
bool RefIsPositive(int ref)
Definition: cp_model_utils.h:34
operations_research::sat::SavedVariable::Get
int Get(PresolveContext *context) const
Definition: presolve_context.cc:27
operations_research::sat::PresolveContext::UpdateRuleStats
void UpdateRuleStats(const std::string &name, int num_times=1)
Definition: presolve_context.cc:284
operations_research::sat::PresolveContext::ConstraintVariableGraphIsUpToDate
bool ConstraintVariableGraphIsUpToDate() const
Definition: presolve_context.cc:351
operations_research::AffineRelation::Relation::representative
int representative
Definition: affine_relation.h:77
operations_research::sat::PresolveContext::VariableIsOnlyUsedInEncoding
bool VariableIsOnlyUsedInEncoding(int ref) const
Definition: presolve_context.cc:214
operations_research::sat::PresolveContext::VariableIsNotUsedAnymore
bool VariableIsNotUsedAnymore(int ref) const
Definition: presolve_context.cc:184
DCHECK_EQ
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:885
b
int64 b
Definition: constraint_solver/table.cc:43
VLOG_IS_ON
#define VLOG_IS_ON(verboselevel)
Definition: vlog_is_on.h:41
operations_research::AffineRelation::IgnoreFromClassSize
void IgnoreFromClassSize(int x)
Definition: affine_relation.h:93
operations_research::Domain::Negation
Domain Negation() const
Returns {x ∈ Int64, ∃ e ∈ D, x = -e}.
Definition: sorted_interval_list.cc:261
operations_research::Domain::AllValues
static Domain AllValues()
Returns the full domain Int64.
Definition: sorted_interval_list.cc:135
operations_research::sat::PresolveContext::CanonicalizeDomainOfSizeTwo
void CanonicalizeDomainOfSizeTwo(int var)
Definition: presolve_context.cc:864
operations_research::sat::PresolveContext::working_model
CpModelProto * working_model
Definition: presolve_context.h:348
CHECK_NE
#define CHECK_NE(val1, val2)
Definition: base/logging.h:698
operations_research::sat::PresolveContext::HasVarValueEncoding
bool HasVarValueEncoding(int ref, int64 value, int *literal=nullptr)
Definition: presolve_context.cc:1063
operations_research::sat::kObjectiveConstraint
constexpr int kObjectiveConstraint
Definition: presolve_context.h:33
operations_research::sat::PresolveContext::InsertVarValueEncoding
void InsertVarValueEncoding(int literal, int ref, int64 value)
Definition: presolve_context.cc:1039
operations_research::sat::PresolveContext::AffineRelationDebugString
std::string AffineRelationDebugString(int ref) const
Definition: presolve_context.cc:759
operations_research::sat::PresolveContext::CanBeUsedAsLiteral
bool CanBeUsedAsLiteral(int ref) const
Definition: presolve_context.cc:83
operations_research::sat::PresolveContext::RefDebugString
std::string RefDebugString(int ref) const
Definition: presolve_context.cc:754
operations_research::AffineRelation
Definition: affine_relation.h:36
literal
Literal literal
Definition: optimization.cc:84
operations_research::sat::PresolveContext::GetAbsRelation
bool GetAbsRelation(int target_ref, int *ref)
Definition: presolve_context.cc:685
operations_research::sat::ReadDomainFromProto
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Definition: cp_model_utils.h:102
operations_research::Domain::Max
int64 Max() const
Returns the max value of the domain.
Definition: sorted_interval_list.cc:211
operations_research::sat::PresolveContext::SetLiteralToTrue
ABSL_MUST_USE_RESULT bool SetLiteralToTrue(int lit)
Definition: presolve_context.cc:280
operations_research::sat::PresolveContext::NewBoolVar
int NewBoolVar()
Definition: presolve_context.cc:40
operations_research::sat::PresolveContext::SetLiteralToFalse
ABSL_MUST_USE_RESULT bool SetLiteralToFalse(int lit)
Definition: presolve_context.cc:274
operations_research::Domain::MultiplicationBy
Domain MultiplicationBy(int64 coeff, bool *exact=nullptr) const
Returns {x ∈ Int64, ∃ e ∈ D, x = e * coeff}.
Definition: sorted_interval_list.cc:361
operations_research::sat::PresolveContext::StoreLiteralImpliesVarNEqValue
bool StoreLiteralImpliesVarNEqValue(int literal, int var, int64 value)
Definition: presolve_context.cc:1055
CHECK
#define CHECK(condition)
Definition: base/logging.h:495
operations_research::sat::PresolveContext::var_to_ub_only_constraints
std::vector< absl::flat_hash_set< int > > var_to_ub_only_constraints
Definition: presolve_context.h:345
operations_research::sat::PresolveContext::enable_stats
bool enable_stats
Definition: presolve_context.h:360
name
const std::string name
Definition: default_search.cc:808
operations_research::Domain::SimplifyUsingImpliedDomain
Domain SimplifyUsingImpliedDomain(const Domain &implied_domain) const
Advanced usage.
Definition: sorted_interval_list.cc:469
operations_research::sat::kAffineRelationConstraint
constexpr int kAffineRelationConstraint
Definition: presolve_context.h:34
operations_research::sat::PresolveContext::ClearStats
void ClearStats()
Definition: presolve_context.cc:31
operations_research::Domain::RelaxIfTooComplex
Domain RelaxIfTooComplex() const
If NumIntervals() is too large, this return a superset of the domain.
Definition: sorted_interval_list.cc:353
gtl::ContainsKey
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:170