OR-Tools  9.1
cp_model_loader.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 <map>
20 #include <memory>
21 #include <set>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 #include "absl/container/flat_hash_map.h"
27 #include "absl/container/flat_hash_set.h"
28 #include "ortools/base/int_type.h"
29 #include "ortools/base/logging.h"
30 #include "ortools/base/map_util.h"
31 #include "ortools/base/stl_util.h"
34 #include "ortools/sat/circuit.h"
38 #include "ortools/sat/cumulative.h"
39 #include "ortools/sat/diffn.h"
42 #include "ortools/sat/integer.h"
44 #include "ortools/sat/intervals.h"
47 #include "ortools/sat/sat_base.h"
49 #include "ortools/sat/sat_solver.h"
50 #include "ortools/sat/symmetry.h"
51 #include "ortools/sat/table.h"
52 #include "ortools/sat/timetable.h"
53 #include "ortools/util/logging.h"
56 
57 namespace operations_research {
58 namespace sat {
59 
60 namespace {
61 
62 template <typename Values>
63 std::vector<int64_t> ValuesFromProto(const Values& values) {
64  return std::vector<int64_t>(values.begin(), values.end());
65 }
66 
67 void ComputeLinearBounds(const LinearConstraintProto& proto,
68  CpModelMapping* mapping, IntegerTrail* integer_trail,
69  int64_t* sum_min, int64_t* sum_max) {
70  *sum_min = 0;
71  *sum_max = 0;
72 
73  for (int i = 0; i < proto.vars_size(); ++i) {
74  const int64_t coeff = proto.coeffs(i);
75  const IntegerVariable var = mapping->Integer(proto.vars(i));
76  const int64_t lb = integer_trail->LowerBound(var).value();
77  const int64_t ub = integer_trail->UpperBound(var).value();
78  if (coeff >= 0) {
79  (*sum_min) += coeff * lb;
80  (*sum_max) += coeff * ub;
81  } else {
82  (*sum_min) += coeff * ub;
83  (*sum_max) += coeff * lb;
84  }
85  }
86 }
87 
88 // We check if the constraint is a sum(ax * xi) == value.
89 bool ConstraintIsEq(const LinearConstraintProto& proto) {
90  return proto.domain_size() == 2 && proto.domain(0) == proto.domain(1);
91 }
92 
93 // We check if the constraint is a sum(ax * xi) != value.
94 bool ConstraintIsNEq(const LinearConstraintProto& proto,
95  CpModelMapping* mapping, IntegerTrail* integer_trail,
96  int64_t* single_value) {
97  int64_t sum_min = 0;
98  int64_t sum_max = 0;
99  ComputeLinearBounds(proto, mapping, integer_trail, &sum_min, &sum_max);
100 
101  const Domain complement =
102  Domain(sum_min, sum_max)
103  .IntersectionWith(ReadDomainFromProto(proto).Complement());
104  if (complement.IsEmpty()) return false;
105  const int64_t value = complement.Min();
106 
107  if (complement.Size() == 1) {
108  if (single_value != nullptr) {
109  *single_value = value;
110  }
111  return true;
112  }
113  return false;
114 }
115 
116 } // namespace
117 
119  bool view_all_booleans_as_integers, Model* m) {
120  auto* mapping = m->GetOrCreate<CpModelMapping>();
121  const int num_proto_variables = model_proto.variables_size();
122 
123  // All [0, 1] variables always have a corresponding Boolean, even if it is
124  // fixed to 0 (domain == [0,0]) or fixed to 1 (domain == [1,1]).
125  {
126  auto* sat_solver = m->GetOrCreate<SatSolver>();
127  CHECK_EQ(sat_solver->NumVariables(), 0);
128 
129  BooleanVariable new_var(0);
130  std::vector<BooleanVariable> false_variables;
131  std::vector<BooleanVariable> true_variables;
132 
133  mapping->booleans_.resize(num_proto_variables, kNoBooleanVariable);
134  mapping->reverse_boolean_map_.resize(num_proto_variables, -1);
135  for (int i = 0; i < num_proto_variables; ++i) {
136  const auto& domain = model_proto.variables(i).domain();
137  if (domain.size() != 2) continue;
138  if (domain[0] >= 0 && domain[1] <= 1) {
139  mapping->booleans_[i] = new_var;
140  mapping->reverse_boolean_map_[new_var] = i;
141  if (domain[1] == 0) {
142  false_variables.push_back(new_var);
143  } else if (domain[0] == 1) {
144  true_variables.push_back(new_var);
145  }
146  ++new_var;
147  }
148  }
149 
150  sat_solver->SetNumVariables(new_var.value());
151  for (const BooleanVariable var : true_variables) {
152  m->Add(ClauseConstraint({sat::Literal(var, true)}));
153  }
154  for (const BooleanVariable var : false_variables) {
155  m->Add(ClauseConstraint({sat::Literal(var, false)}));
156  }
157  }
158 
159  // Compute the list of positive variable reference for which we need to
160  // create an IntegerVariable.
161  std::vector<int> var_to_instantiate_as_integer;
162  if (view_all_booleans_as_integers) {
163  var_to_instantiate_as_integer.resize(num_proto_variables);
164  for (int i = 0; i < num_proto_variables; ++i) {
165  var_to_instantiate_as_integer[i] = i;
166  }
167  } else {
168  // Compute the integer variable references used by the model.
169  absl::flat_hash_set<int> used_variables;
170 
171  IndexReferences refs;
172  for (int c = 0; c < model_proto.constraints_size(); ++c) {
175  for (const int ref : refs.variables) {
176  used_variables.insert(PositiveRef(ref));
177  }
178  }
179 
180  // Add the objectives variables that needs to be referenceable as integer
181  // even if they are only used as Booleans.
182  if (model_proto.has_objective()) {
183  for (const int obj_var : model_proto.objective().vars()) {
184  used_variables.insert(PositiveRef(obj_var));
185  }
186  }
187 
188  // Make sure any unused variable, that is not already a Boolean is
189  // considered "used".
190  for (int i = 0; i < num_proto_variables; ++i) {
191  if (mapping->booleans_[i] == kNoBooleanVariable) {
192  used_variables.insert(i);
193  }
194  }
195 
196  // We want the variable in the problem order.
197  var_to_instantiate_as_integer.assign(used_variables.begin(),
198  used_variables.end());
199  gtl::STLSortAndRemoveDuplicates(&var_to_instantiate_as_integer);
200  }
201  mapping->integers_.resize(num_proto_variables, kNoIntegerVariable);
202 
203  auto* integer_trail = m->GetOrCreate<IntegerTrail>();
204  integer_trail->ReserveSpaceForNumVariables(
205  var_to_instantiate_as_integer.size());
206  mapping->reverse_integer_map_.resize(2 * var_to_instantiate_as_integer.size(),
207  -1);
208  for (const int i : var_to_instantiate_as_integer) {
209  const auto& var_proto = model_proto.variables(i);
210  mapping->integers_[i] =
211  integer_trail->AddIntegerVariable(ReadDomainFromProto(var_proto));
212  DCHECK_LT(mapping->integers_[i], mapping->reverse_integer_map_.size());
213  mapping->reverse_integer_map_[mapping->integers_[i]] = i;
214  }
215 
216  auto* encoder = m->GetOrCreate<IntegerEncoder>();
217  auto* intervals_repository = m->GetOrCreate<IntervalsRepository>();
218 
219  // Link any variable that has both views.
220  for (int i = 0; i < num_proto_variables; ++i) {
221  if (mapping->integers_[i] == kNoIntegerVariable) continue;
222  if (mapping->booleans_[i] == kNoBooleanVariable) continue;
223 
224  // Associate with corresponding integer variable.
225  encoder->AssociateToIntegerEqualValue(
226  sat::Literal(mapping->booleans_[i], true), mapping->integers_[i],
227  IntegerValue(1));
228  }
229 
230  // Create the interval variables.
231  mapping->intervals_.resize(model_proto.constraints_size(),
233  for (int c = 0; c < model_proto.constraints_size(); ++c) {
235  if (ct.constraint_case() != ConstraintProto::ConstraintCase::kInterval) {
236  continue;
237  }
238  if (HasEnforcementLiteral(ct)) {
239  const sat::Literal enforcement_literal =
240  mapping->Literal(ct.enforcement_literal(0));
241  // TODO(user): Fix the constant variable situation. An optional interval
242  // with constant start/end or size cannot share the same constant
243  // variable if it is used in non-optional situation.
244  if (ct.interval().has_start_view()) {
245  mapping->intervals_[c] = intervals_repository->CreateInterval(
246  mapping->LoadAffineView(ct.interval().start_view()),
247  mapping->LoadAffineView(ct.interval().end_view()),
248  mapping->LoadAffineView(ct.interval().size_view()),
249  enforcement_literal.Index(),
250  /*add_linear_relation=*/false);
251  } else {
252  mapping->intervals_[c] = m->Add(NewOptionalInterval(
253  mapping->Integer(ct.interval().start()),
254  mapping->Integer(ct.interval().end()),
255  mapping->Integer(ct.interval().size()), enforcement_literal));
256  }
257  } else {
258  if (ct.interval().has_start_view()) {
259  mapping->intervals_[c] = intervals_repository->CreateInterval(
260  mapping->LoadAffineView(ct.interval().start_view()),
261  mapping->LoadAffineView(ct.interval().end_view()),
262  mapping->LoadAffineView(ct.interval().size_view()), kNoLiteralIndex,
263  /*add_linear_relation=*/false);
264  } else {
265  mapping->intervals_[c] =
266  m->Add(NewInterval(mapping->Integer(ct.interval().start()),
267  mapping->Integer(ct.interval().end()),
268  mapping->Integer(ct.interval().size())));
269  }
270  }
271  mapping->already_loaded_ct_.insert(&ct);
272  }
273 }
274 
276  auto* mapping = m->GetOrCreate<CpModelMapping>();
277  const SymmetryProto symmetry = model_proto.symmetry();
278  if (symmetry.permutations().empty()) return;
279 
280  auto* sat_solver = m->GetOrCreate<SatSolver>();
281  auto* symmetry_handler = m->GetOrCreate<SymmetryPropagator>();
282  sat_solver->AddPropagator(symmetry_handler);
283  const int num_literals = 2 * sat_solver->NumVariables();
284 
285  for (const SparsePermutationProto& perm : symmetry.permutations()) {
286  bool all_bool = true;
287  for (const int var : perm.support()) {
288  if (!mapping->IsBoolean(var)) {
289  all_bool = false;
290  break;
291  }
292  }
293  if (!all_bool) continue;
294 
295  // Convert the variable symmetry to a "literal" one.
296  auto literal_permutation =
297  absl::make_unique<SparsePermutation>(num_literals);
298  int support_index = 0;
299  const int num_cycle = perm.cycle_sizes().size();
300  for (int i = 0; i < num_cycle; ++i) {
301  const int size = perm.cycle_sizes(i);
302  const int saved_support_index = support_index;
303  for (int j = 0; j < size; ++j) {
304  const int var = perm.support(support_index++);
305  literal_permutation->AddToCurrentCycle(
306  mapping->Literal(var).Index().value());
307  }
308  literal_permutation->CloseCurrentCycle();
309 
310  // Note that we also need to add the corresponding cycle for the negated
311  // literals.
312  support_index = saved_support_index;
313  for (int j = 0; j < size; ++j) {
314  const int var = perm.support(support_index++);
315  literal_permutation->AddToCurrentCycle(
316  mapping->Literal(var).NegatedIndex().value());
317  }
318  literal_permutation->CloseCurrentCycle();
319  }
320  symmetry_handler->AddSymmetry(std::move(literal_permutation));
321  }
322 
323  SOLVER_LOG(m->GetOrCreate<SolverLogger>(), "Added ",
324  symmetry_handler->num_permutations(),
325  " symmetry to the SAT solver.");
326 }
327 
328 // The logic assumes that the linear constraints have been presolved, so that
329 // equality with a domain bound have been converted to <= or >= and so that we
330 // never have any trivial inequalities.
331 //
332 // TODO(user): Regroup/presolve two encoding like b => x > 2 and the same
333 // Boolean b => x > 5. These shouldn't happen if we merge linear constraints.
335  auto* mapping = m->GetOrCreate<CpModelMapping>();
336  auto* encoder = m->GetOrCreate<IntegerEncoder>();
337  auto* integer_trail = m->GetOrCreate<IntegerTrail>();
338  auto* sat_solver = m->GetOrCreate<SatSolver>();
339 
340  // TODO(user): Debug what makes it unsat at this point.
341  if (sat_solver->IsModelUnsat()) return;
342 
343  // Detection of literal equivalent to (i_var == value). We collect all the
344  // half-reified constraint lit => equality or lit => inequality for a given
345  // variable, and we will later sort them to detect equivalence.
346  struct EqualityDetectionHelper {
347  const ConstraintProto* ct;
349  int64_t value;
350  bool is_equality; // false if != instead.
351 
352  bool operator<(const EqualityDetectionHelper& o) const {
353  if (literal.Variable() == o.literal.Variable()) {
354  if (value == o.value) return is_equality && !o.is_equality;
355  return value < o.value;
356  }
357  return literal.Variable() < o.literal.Variable();
358  }
359  };
360  std::vector<std::vector<EqualityDetectionHelper>> var_to_equalities(
362 
363  // TODO(user): We will re-add the same implied bounds during probing, so
364  // it might not be necessary to do that here. Also, it might be too early
365  // if some of the literal view used in the LP are created later, but that
366  // should be fixable via calls to implied_bounds->NotifyNewIntegerView().
367  auto* implied_bounds = m->GetOrCreate<ImpliedBounds>();
368 
369  // Detection of literal equivalent to (i_var >= bound). We also collect
370  // all the half-refied part and we will sort the vector for detection of the
371  // equivalence.
372  struct InequalityDetectionHelper {
373  const ConstraintProto* ct;
375  IntegerLiteral i_lit;
376 
377  bool operator<(const InequalityDetectionHelper& o) const {
378  if (literal.Variable() == o.literal.Variable()) {
379  return i_lit.var < o.i_lit.var;
380  }
381  return literal.Variable() < o.literal.Variable();
382  }
383  };
384  std::vector<InequalityDetectionHelper> inequalities;
385 
386  // Loop over all constraints and fill var_to_equalities and inequalities.
387  for (const ConstraintProto& ct : model_proto.constraints()) {
388  if (ct.constraint_case() != ConstraintProto::ConstraintCase::kLinear) {
389  continue;
390  }
391  if (ct.enforcement_literal().size() != 1) continue;
392  if (ct.linear().vars_size() != 1) continue;
393 
394  // ct is a linear constraint with one term and one enforcement literal.
395  const sat::Literal enforcement_literal =
396  mapping->Literal(ct.enforcement_literal(0));
397  if (sat_solver->Assignment().LiteralIsFalse(enforcement_literal)) continue;
398 
399  const int ref = ct.linear().vars(0);
400  const int var = PositiveRef(ref);
401 
403  const Domain domain_if_enforced =
404  ReadDomainFromProto(ct.linear())
405  .InverseMultiplicationBy(ct.linear().coeffs(0) *
406  (RefIsPositive(ref) ? 1 : -1));
407 
408  if (domain_if_enforced.IsEmpty()) {
409  if (!sat_solver->AddUnitClause(enforcement_literal.Negated())) return;
410  continue;
411  }
412 
413  // Detect enforcement_literal => (var >= value or var <= value).
414  if (domain_if_enforced.NumIntervals() == 1) {
415  if (domain_if_enforced.Max() >= domain.Max() &&
416  domain_if_enforced.Min() > domain.Min()) {
417  inequalities.push_back({&ct, enforcement_literal,
419  mapping->Integer(var),
420  IntegerValue(domain_if_enforced.Min()))});
421  } else if (domain_if_enforced.Min() <= domain.Min() &&
422  domain_if_enforced.Max() < domain.Max()) {
423  inequalities.push_back({&ct, enforcement_literal,
425  mapping->Integer(var),
426  IntegerValue(domain_if_enforced.Max()))});
427  }
428  }
429 
430  // Detect implied bounds. The test is less strict than the above
431  // test.
432  if (domain_if_enforced.Min() > domain.Min()) {
433  implied_bounds->Add(
434  enforcement_literal,
436  mapping->Integer(var), IntegerValue(domain_if_enforced.Min())));
437  }
438  if (domain_if_enforced.Max() < domain.Max()) {
439  implied_bounds->Add(
440  enforcement_literal,
441  IntegerLiteral::LowerOrEqual(mapping->Integer(var),
442  IntegerValue(domain_if_enforced.Max())));
443  }
444 
445  // Detect enforcement_literal => (var == value or var != value).
446  //
447  // Note that for domain with 2 values like [0, 1], we will detect both ==
448  // 0 and != 1. Similarly, for a domain in [min, max], we should both
449  // detect (== min) and (<= min), and both detect (== max) and (>= max).
450  {
451  const Domain inter = domain.IntersectionWith(domain_if_enforced);
452  if (!inter.IsEmpty() && inter.Min() == inter.Max()) {
453  var_to_equalities[var].push_back(
454  {&ct, enforcement_literal, inter.Min(), true});
455  if (domain.Contains(inter.Min())) {
456  mapping->variables_to_encoded_values_[var].insert(inter.Min());
457  }
458  }
459  }
460  {
461  const Domain inter =
462  domain.IntersectionWith(domain_if_enforced.Complement());
463  if (!inter.IsEmpty() && inter.Min() == inter.Max()) {
464  var_to_equalities[var].push_back(
465  {&ct, enforcement_literal, inter.Min(), false});
466  if (domain.Contains(inter.Min())) {
467  mapping->variables_to_encoded_values_[var].insert(inter.Min());
468  }
469  }
470  }
471  }
472 
473  // Detect Literal <=> X >= value
474  int num_inequalities = 0;
475  std::sort(inequalities.begin(), inequalities.end());
476  for (int i = 0; i + 1 < inequalities.size(); i++) {
477  if (inequalities[i].literal != inequalities[i + 1].literal.Negated()) {
478  continue;
479  }
480 
481  // TODO(user): In these cases, we could fix the enforcement literal right
482  // away or ignore the constraint. Note that it will be done later anyway
483  // though.
484  if (integer_trail->IntegerLiteralIsTrue(inequalities[i].i_lit) ||
485  integer_trail->IntegerLiteralIsFalse(inequalities[i].i_lit)) {
486  continue;
487  }
488  if (integer_trail->IntegerLiteralIsTrue(inequalities[i + 1].i_lit) ||
489  integer_trail->IntegerLiteralIsFalse(inequalities[i + 1].i_lit)) {
490  continue;
491  }
492 
493  const auto pair_a = encoder->Canonicalize(inequalities[i].i_lit);
494  const auto pair_b = encoder->Canonicalize(inequalities[i + 1].i_lit);
495  if (pair_a.first == pair_b.second) {
496  ++num_inequalities;
497  encoder->AssociateToIntegerLiteral(inequalities[i].literal,
498  inequalities[i].i_lit);
499  mapping->already_loaded_ct_.insert(inequalities[i].ct);
500  mapping->already_loaded_ct_.insert(inequalities[i + 1].ct);
501  }
502  }
503 
504  // Encode the half-inequalities.
505  int num_half_inequalities = 0;
506  for (const auto inequality : inequalities) {
507  if (mapping->ConstraintIsAlreadyLoaded(inequality.ct)) continue;
508  m->Add(
509  Implication(inequality.literal,
510  encoder->GetOrCreateAssociatedLiteral(inequality.i_lit)));
511  if (sat_solver->IsModelUnsat()) return;
512 
513  ++num_half_inequalities;
514  mapping->already_loaded_ct_.insert(inequality.ct);
515  mapping->is_half_encoding_ct_.insert(inequality.ct);
516  }
517 
518  if (!inequalities.empty()) {
519  VLOG(1) << num_inequalities << " literals associated to VAR >= value, and "
520  << num_half_inequalities << " half-associations.";
521  }
522 
523  // Detect Literal <=> X == value and associate them in the IntegerEncoder.
524  //
525  // TODO(user): Fully encode variable that are almost fully encoded?
526  int num_constraints = 0;
527  int num_equalities = 0;
528  int num_half_equalities = 0;
529  int num_fully_encoded = 0;
530  int num_partially_encoded = 0;
531  for (int i = 0; i < var_to_equalities.size(); ++i) {
532  std::vector<EqualityDetectionHelper>& encoding = var_to_equalities[i];
533  std::sort(encoding.begin(), encoding.end());
534  if (encoding.empty()) continue;
535  num_constraints += encoding.size();
536 
537  absl::flat_hash_set<int64_t> values;
538  for (int j = 0; j + 1 < encoding.size(); j++) {
539  if ((encoding[j].value != encoding[j + 1].value) ||
540  (encoding[j].literal != encoding[j + 1].literal.Negated()) ||
541  (encoding[j].is_equality != true) ||
542  (encoding[j + 1].is_equality != false)) {
543  continue;
544  }
545 
546  ++num_equalities;
547  encoder->AssociateToIntegerEqualValue(encoding[j].literal,
548  mapping->integers_[i],
549  IntegerValue(encoding[j].value));
550  mapping->already_loaded_ct_.insert(encoding[j].ct);
551  mapping->already_loaded_ct_.insert(encoding[j + 1].ct);
552  values.insert(encoding[j].value);
553  }
554 
555  // TODO(user): Try to remove it. Normally we caught UNSAT above, but
556  // tests are very flaky (it only happens in parallel). Keeping it there for
557  // the time being.
558  if (sat_solver->IsModelUnsat()) return;
559 
560  // Encode the half-equalities.
561  //
562  // TODO(user): delay this after PropagateEncodingFromEquivalenceRelations()?
563  // Otherwise we might create new Boolean variables for no reason. Note
564  // however, that in the presolve, we should only use the "representative" in
565  // linear constraints, so we should be fine.
566  for (const auto equality : encoding) {
567  if (mapping->ConstraintIsAlreadyLoaded(equality.ct)) continue;
568  const class Literal eq = encoder->GetOrCreateLiteralAssociatedToEquality(
569  mapping->integers_[i], IntegerValue(equality.value));
570  if (equality.is_equality) {
571  m->Add(Implication(equality.literal, eq));
572  } else {
573  m->Add(Implication(equality.literal, eq.Negated()));
574  }
575 
576  ++num_half_equalities;
577  mapping->already_loaded_ct_.insert(equality.ct);
578  mapping->is_half_encoding_ct_.insert(equality.ct);
579  }
580 
581  // Update stats.
582  if (VLOG_IS_ON(1)) {
583  if (encoder->VariableIsFullyEncoded(mapping->integers_[i])) {
584  ++num_fully_encoded;
585  } else {
586  ++num_partially_encoded;
587  }
588  }
589  }
590 
591  if (num_constraints > 0) {
592  VLOG(1) << num_equalities << " literals associated to VAR == value, and "
593  << num_half_equalities << " half-associations.";
594  }
595  if (num_fully_encoded > 0) {
596  VLOG(1) << "num_fully_encoded_variables: " << num_fully_encoded;
597  }
598  if (num_partially_encoded > 0) {
599  VLOG(1) << "num_partially_encoded_variables: " << num_partially_encoded;
600  }
601 }
602 
604  Model* m) {
605  auto* mapping = m->GetOrCreate<CpModelMapping>();
606  auto* encoder = m->GetOrCreate<IntegerEncoder>();
607  auto* sat_solver = m->GetOrCreate<SatSolver>();
608 
609  // Loop over all constraints and find affine ones.
610  int64_t num_associations = 0;
611  int64_t num_set_to_false = 0;
612  for (const ConstraintProto& ct : model_proto.constraints()) {
613  if (!ct.enforcement_literal().empty()) continue;
614  if (ct.constraint_case() != ConstraintProto::kLinear) continue;
615  if (ct.linear().vars_size() != 2) continue;
616  if (!ConstraintIsEq(ct.linear())) continue;
617 
618  const IntegerValue rhs(ct.linear().domain(0));
619 
620  // Make sure the coefficient are positive.
621  IntegerVariable var1 = mapping->Integer(ct.linear().vars(0));
622  IntegerVariable var2 = mapping->Integer(ct.linear().vars(1));
623  IntegerValue coeff1(ct.linear().coeffs(0));
624  IntegerValue coeff2(ct.linear().coeffs(1));
625  if (coeff1 < 0) {
626  var1 = NegationOf(var1);
627  coeff1 = -coeff1;
628  }
629  if (coeff2 < 0) {
630  var2 = NegationOf(var2);
631  coeff2 = -coeff2;
632  }
633 
634  // TODO(user): This is not supposed to happen, but apparently it did on
635  // once on routing_GCM_0001_sat.fzn. Investigate and fix.
636  if (coeff1 == 0 || coeff2 == 0) continue;
637 
638  // We first map the >= literals.
639  // It is important to do that first, since otherwise mapping a == literal
640  // might creates the underlying >= and <= literals.
641  for (int i = 0; i < 2; ++i) {
642  for (const auto value_literal :
643  encoder->PartialGreaterThanEncoding(var1)) {
644  const IntegerValue value1 = value_literal.first;
645  const IntegerValue bound2 = FloorRatio(rhs - value1 * coeff1, coeff2);
646  ++num_associations;
647  encoder->AssociateToIntegerLiteral(
648  value_literal.second, IntegerLiteral::LowerOrEqual(var2, bound2));
649  }
650  std::swap(var1, var2);
651  std::swap(coeff1, coeff2);
652  }
653 
654  // Same for the == literals.
655  //
656  // TODO(user): This is similar to LoadEquivalenceAC() for unreified
657  // constraints, but when the later is called, more encoding might have taken
658  // place.
659  for (int i = 0; i < 2; ++i) {
660  for (const auto value_literal : encoder->PartialDomainEncoding(var1)) {
661  const IntegerValue value1 = value_literal.value;
662  const IntegerValue intermediate = rhs - value1 * coeff1;
663  if (intermediate % coeff2 != 0) {
664  // Using this function deals properly with UNSAT.
665  ++num_set_to_false;
666  sat_solver->AddUnitClause(value_literal.literal.Negated());
667  continue;
668  }
669  ++num_associations;
670  encoder->AssociateToIntegerEqualValue(value_literal.literal, var2,
671  intermediate / coeff2);
672  }
673  std::swap(var1, var2);
674  std::swap(coeff1, coeff2);
675  }
676  }
677 
678  if (num_associations > 0) {
679  VLOG(1) << "Num associations from equivalences = " << num_associations;
680  }
681  if (num_set_to_false > 0) {
682  VLOG(1) << "Num literals set to false from equivalences = "
683  << num_set_to_false;
684  }
685 }
686 
688  auto* mapping = m->GetOrCreate<CpModelMapping>();
690  if (!parameters.use_optional_variables()) return;
691  if (parameters.enumerate_all_solutions()) return;
692 
693  // The variables from the objective cannot be marked as optional!
694  const int num_proto_variables = model_proto.variables_size();
695  std::vector<bool> already_seen(num_proto_variables, false);
696  if (model_proto.has_objective()) {
697  for (const int ref : model_proto.objective().vars()) {
698  already_seen[PositiveRef(ref)] = true;
699  }
700  }
701 
702  // Compute for each variables the intersection of the enforcement literals
703  // of the constraints in which they appear.
704  //
705  // TODO(user): This deals with the simplest cases, but we could try to
706  // detect literals that implies all the constraints in which a variable
707  // appear to false. This can be done with a LCA computation in the tree of
708  // Boolean implication (once the presolve remove cycles). Not sure if we can
709  // properly exploit that afterwards though. Do some research!
710  std::vector<std::vector<int>> enforcement_intersection(num_proto_variables);
711  std::set<int> literals_set;
712  for (int c = 0; c < model_proto.constraints_size(); ++c) {
714  if (ct.enforcement_literal().empty()) {
715  for (const int var : UsedVariables(ct)) {
716  already_seen[var] = true;
717  enforcement_intersection[var].clear();
718  }
719  } else {
720  literals_set.clear();
721  literals_set.insert(ct.enforcement_literal().begin(),
722  ct.enforcement_literal().end());
723  for (const int var : UsedVariables(ct)) {
724  if (!already_seen[var]) {
725  enforcement_intersection[var].assign(ct.enforcement_literal().begin(),
726  ct.enforcement_literal().end());
727  } else {
728  // Take the intersection.
729  std::vector<int>& vector_ref = enforcement_intersection[var];
730  int new_size = 0;
731  for (const int literal : vector_ref) {
732  if (gtl::ContainsKey(literals_set, literal)) {
733  vector_ref[new_size++] = literal;
734  }
735  }
736  vector_ref.resize(new_size);
737  }
738  already_seen[var] = true;
739  }
740  }
741  }
742 
743  // Auto-detect optional variables.
744  int num_optionals = 0;
745  auto* integer_trail = m->GetOrCreate<IntegerTrail>();
746  for (int var = 0; var < num_proto_variables; ++var) {
747  const IntegerVariableProto& var_proto = model_proto.variables(var);
748  const int64_t min = var_proto.domain(0);
749  const int64_t max = var_proto.domain(var_proto.domain().size() - 1);
750  if (min == max) continue;
751  if (min == 0 && max == 1) continue;
752  if (enforcement_intersection[var].empty()) continue;
753 
754  ++num_optionals;
755  integer_trail->MarkIntegerVariableAsOptional(
756  mapping->Integer(var),
757  mapping->Literal(enforcement_intersection[var].front()));
758  }
759  VLOG(2) << "Auto-detected " << num_optionals << " optional variables.";
760 }
761 
762 // ============================================================================
763 // A class that detects when variables should be fully encoded by computing a
764 // fixed point. It also fully encodes such variables.
765 // ============================================================================
766 
768  public:
770  : model_proto_(model_proto),
771  parameters_(*(model->GetOrCreate<SatParameters>())),
772  model_(model),
773  mapping_(model->GetOrCreate<CpModelMapping>()),
774  integer_encoder_(model->GetOrCreate<IntegerEncoder>()),
775  integer_trail_(model->GetOrCreate<IntegerTrail>()) {}
776 
777  void ComputeFixedPoint();
778 
779  private:
780  DEFINE_INT_TYPE(ConstraintIndex, int32_t);
781 
782  // Constraint ct is interested by (full-encoding) state of variable.
783  void Register(ConstraintIndex ct_index, int variable) {
784  variable = PositiveRef(variable);
785  constraint_is_registered_[ct_index] = true;
786  if (variable_watchers_.size() <= variable) {
787  variable_watchers_.resize(variable + 1);
788  variable_was_added_in_to_propagate_.resize(variable + 1);
789  }
790  variable_watchers_[variable].push_back(ct_index);
791  }
792 
793  void AddVariableToPropagationQueue(int variable) {
794  variable = PositiveRef(variable);
795  if (variable_was_added_in_to_propagate_.size() <= variable) {
796  variable_watchers_.resize(variable + 1);
797  variable_was_added_in_to_propagate_.resize(variable + 1);
798  }
799  if (!variable_was_added_in_to_propagate_[variable]) {
800  variable_was_added_in_to_propagate_[variable] = true;
801  variables_to_propagate_.push_back(variable);
802  }
803  }
804 
805  // Note that we always consider a fixed variable to be fully encoded here.
806  const bool IsFullyEncoded(int v) {
807  const IntegerVariable variable = mapping_->Integer(v);
808  if (variable == kNoIntegerVariable) return false;
809  return integer_trail_->IsFixed(variable) ||
810  integer_encoder_->VariableIsFullyEncoded(variable);
811  }
812 
813  const bool VariableIsFixed(int v) {
814  const IntegerVariable variable = mapping_->Integer(v);
815  if (v == kNoIntegerVariable) return false;
816  return integer_trail_->IsFixed(variable);
817  }
818 
819  void FullyEncode(int v) {
820  v = PositiveRef(v);
821  const IntegerVariable variable = mapping_->Integer(v);
822  if (v == kNoIntegerVariable) return;
823  if (!integer_trail_->IsFixed(variable)) {
824  model_->Add(FullyEncodeVariable(variable));
825  }
826  AddVariableToPropagationQueue(v);
827  }
828 
829  bool ProcessConstraint(ConstraintIndex ct_index);
830  bool ProcessElement(ConstraintIndex ct_index);
831  bool ProcessTable(ConstraintIndex ct_index);
832  bool ProcessAutomaton(ConstraintIndex ct_index);
833  bool ProcessLinear(ConstraintIndex ct_index);
834 
835  const CpModelProto& model_proto_;
836  const SatParameters& parameters_;
837 
838  Model* model_;
839  CpModelMapping* mapping_;
840  IntegerEncoder* integer_encoder_;
841  IntegerTrail* integer_trail_;
842 
843  std::vector<bool> variable_was_added_in_to_propagate_;
844  std::vector<int> variables_to_propagate_;
845  std::vector<std::vector<ConstraintIndex>> variable_watchers_;
846 
847  absl::StrongVector<ConstraintIndex, bool> constraint_is_finished_;
848  absl::StrongVector<ConstraintIndex, bool> constraint_is_registered_;
849 
850  absl::flat_hash_map<int, absl::flat_hash_set<int>>
851  variables_to_equal_or_diff_variables_;
852 };
853 
854 // We only add to the propagation queue variable that are fully encoded.
855 // Note that if a variable was already added once, we never add it again.
857  const int num_constraints = model_proto_.constraints_size();
858  const int num_vars = model_proto_.variables_size();
859  constraint_is_finished_.assign(num_constraints, false);
860  constraint_is_registered_.assign(num_constraints, false);
861 
862  // Process all constraint once.
863  for (ConstraintIndex ct_index(0); ct_index < num_constraints; ++ct_index) {
864  constraint_is_finished_[ct_index] = ProcessConstraint(ct_index);
865  }
866 
867  // We run a heuristics to decide if we want to fully encode a variable or not.
868  // We decide to fully encode a variable if:
869  // - a variable appears in enough a1 * x1 + a2 + x2 ==/!= value and the
870  // domain is small.
871  // - the number of values that appears in b => x ==/!= value that are not
872  // the bounds of the variables is more that half the size of the domain.
873  // . - the size of the domain is > 2
874  int num_variables_fully_encoded_by_heuristics = 0;
875  for (int var = 0; var < num_vars; ++var) {
876  if (!mapping_->IsInteger(var) || IsFullyEncoded(var)) continue;
877  const IntegerVariableProto& int_var_proto = model_proto_.variables(var);
878  const Domain domain = ReadDomainFromProto(int_var_proto);
879  int64_t domain_size = domain.Size();
880  int64_t num_diff_or_equal_var_constraints = 0;
881  int64_t num_potential_encoded_values_without_bounds = 0;
882 
883  if (domain_size <= 2) continue;
884 
885  const absl::flat_hash_set<int64_t>& value_set =
886  mapping_->PotentialEncodedValues(var);
887  for (const int value : value_set) {
888  if (value > domain.Min() && value < domain.Max() &&
889  domain.Contains(value)) {
890  num_potential_encoded_values_without_bounds++;
891  }
892  }
893 
894  const auto& it = variables_to_equal_or_diff_variables_.find(var);
895  if (it != variables_to_equal_or_diff_variables_.end()) {
896  num_diff_or_equal_var_constraints = it->second.size();
897  }
898 
899  if (num_potential_encoded_values_without_bounds >= domain_size / 2 ||
900  (num_diff_or_equal_var_constraints >= domain_size / 2 &&
901  domain_size < 16)) {
902  VLOG(3) << model_proto_.variables(var).ShortDebugString()
903  << " is encoded with "
904  << num_potential_encoded_values_without_bounds
905  << " unary constraints, and " << num_diff_or_equal_var_constraints
906  << " binary constraints on a domain of size " << domain_size;
907  FullyEncode(var);
908  num_variables_fully_encoded_by_heuristics++;
909  }
910  }
911  if (num_variables_fully_encoded_by_heuristics > 0) {
912  VLOG(2) << num_variables_fully_encoded_by_heuristics
913  << " variables fully encoded after model introspection.";
914  }
915 
916  // Make sure all fully encoded variables of interest are in the queue.
917  for (int v = 0; v < variable_watchers_.size(); v++) {
918  if (!variable_watchers_[v].empty() && IsFullyEncoded(v)) {
919  AddVariableToPropagationQueue(v);
920  }
921  }
922 
923  // Loop until no additional variable can be fully encoded.
924  while (!variables_to_propagate_.empty()) {
925  const int variable = variables_to_propagate_.back();
926  variables_to_propagate_.pop_back();
927  for (const ConstraintIndex ct_index : variable_watchers_[variable]) {
928  if (constraint_is_finished_[ct_index]) continue;
929  constraint_is_finished_[ct_index] = ProcessConstraint(ct_index);
930  }
931  }
932 }
933 
934 // Returns true if the constraint has finished encoding what it wants.
935 bool FullEncodingFixedPointComputer::ProcessConstraint(
936  ConstraintIndex ct_index) {
937  const ConstraintProto& ct = model_proto_.constraints(ct_index.value());
938  switch (ct.constraint_case()) {
939  case ConstraintProto::ConstraintProto::kElement:
940  return ProcessElement(ct_index);
941  case ConstraintProto::ConstraintProto::kTable:
942  return ProcessTable(ct_index);
943  case ConstraintProto::ConstraintProto::kAutomaton:
944  return ProcessAutomaton(ct_index);
945  case ConstraintProto::ConstraintProto::kLinear:
946  return ProcessLinear(ct_index);
947  default:
948  return true;
949  }
950 }
951 
952 bool FullEncodingFixedPointComputer::ProcessElement(ConstraintIndex ct_index) {
953  const ConstraintProto& ct = model_proto_.constraints(ct_index.value());
954 
955  // Index must always be full encoded.
956  FullyEncode(ct.element().index());
957 
958  const int target = ct.element().target();
959 
960  // If target is fixed, do not encode variables.
961  if (VariableIsFixed(target)) return true;
962 
963  // If target is a constant or fully encoded, variables must be fully encoded.
964  if (IsFullyEncoded(target)) {
965  for (const int v : ct.element().vars()) FullyEncode(v);
966  }
967 
968  // If all non-target variables are fully encoded, target must be too.
969  bool all_variables_are_fully_encoded = true;
970  for (const int v : ct.element().vars()) {
971  if (v == target) continue;
972  if (!IsFullyEncoded(v)) {
973  all_variables_are_fully_encoded = false;
974  break;
975  }
976  }
977  if (all_variables_are_fully_encoded) {
978  if (!IsFullyEncoded(target)) FullyEncode(target);
979  return true;
980  }
981 
982  // If some variables are not fully encoded, register on those.
983  if (constraint_is_registered_[ct_index]) {
984  for (const int v : ct.element().vars()) Register(ct_index, v);
985  Register(ct_index, target);
986  }
987  return false;
988 }
989 
990 bool FullEncodingFixedPointComputer::ProcessTable(ConstraintIndex ct_index) {
991  const ConstraintProto& ct = model_proto_.constraints(ct_index.value());
992 
993  if (ct.table().negated()) return true;
994 
995  for (const int variable : ct.table().vars()) {
996  FullyEncode(variable);
997  }
998 
999  return true;
1000 }
1001 
1002 bool FullEncodingFixedPointComputer::ProcessAutomaton(
1003  ConstraintIndex ct_index) {
1004  const ConstraintProto& ct = model_proto_.constraints(ct_index.value());
1005  for (const int variable : ct.automaton().vars()) {
1006  FullyEncode(variable);
1007  }
1008  return true;
1009 }
1010 
1011 bool FullEncodingFixedPointComputer::ProcessLinear(ConstraintIndex ct_index) {
1012  // We are only interested in linear equations of the form:
1013  // [b =>] a1 * x1 + a2 * x2 ==|!= value
1014  const ConstraintProto& ct = model_proto_.constraints(ct_index.value());
1015  if (parameters_.boolean_encoding_level() == 0 ||
1016  ct.linear().vars_size() != 2) {
1017  return true;
1018  }
1019 
1020  if (!ConstraintIsEq(ct.linear()) &&
1021  !ConstraintIsNEq(ct.linear(), mapping_, integer_trail_, nullptr)) {
1022  return true;
1023  }
1024 
1025  const int var0 = ct.linear().vars(0);
1026  const int var1 = ct.linear().vars(1);
1027  if (!IsFullyEncoded(var0)) {
1028  variables_to_equal_or_diff_variables_[var0].insert(var1);
1029  }
1030  if (!IsFullyEncoded(var1)) {
1031  variables_to_equal_or_diff_variables_[var1].insert(var0);
1032  }
1033  return true;
1034 }
1035 
1038  fixpoint.ComputeFixedPoint();
1039 }
1040 
1042  Model* m) {
1043  if (model_proto.search_strategy().empty()) return;
1044 
1045  auto* mapping = m->GetOrCreate<CpModelMapping>();
1046  auto* integer_trail = m->GetOrCreate<IntegerTrail>();
1047  for (const DecisionStrategyProto& strategy : model_proto.search_strategy()) {
1048  if (strategy.domain_reduction_strategy() ==
1050  for (const int ref : strategy.variables()) {
1051  if (!mapping->IsInteger(ref)) return;
1052  const IntegerVariable variable = mapping->Integer(PositiveRef(ref));
1053  if (!integer_trail->IsFixed(variable)) {
1054  m->Add(FullyEncodeVariable(variable));
1055  }
1056  }
1057  }
1058  }
1059 }
1060 
1061 // ============================================================================
1062 // Constraint loading functions.
1063 // ============================================================================
1064 
1066  auto* mapping = m->GetOrCreate<CpModelMapping>();
1067  std::vector<Literal> literals = mapping->Literals(ct.bool_or().literals());
1068  for (const int ref : ct.enforcement_literal()) {
1069  literals.push_back(mapping->Literal(ref).Negated());
1070  }
1071  m->Add(ClauseConstraint(literals));
1072 }
1073 
1075  auto* mapping = m->GetOrCreate<CpModelMapping>();
1076  std::vector<Literal> literals;
1077  for (const int ref : ct.enforcement_literal()) {
1078  literals.push_back(mapping->Literal(ref).Negated());
1079  }
1080  auto* sat_solver = m->GetOrCreate<SatSolver>();
1081  for (const Literal literal : mapping->Literals(ct.bool_and().literals())) {
1082  literals.push_back(literal);
1083  sat_solver->AddProblemClause(literals);
1084  literals.pop_back();
1085  }
1086 }
1087 
1089  auto* mapping = m->GetOrCreate<CpModelMapping>();
1090  CHECK(!HasEnforcementLiteral(ct)) << "Not supported.";
1091  m->Add(AtMostOneConstraint(mapping->Literals(ct.at_most_one().literals())));
1092 }
1093 
1095  auto* mapping = m->GetOrCreate<CpModelMapping>();
1096  CHECK(!HasEnforcementLiteral(ct)) << "Not supported.";
1097  m->Add(ExactlyOneConstraint(mapping->Literals(ct.exactly_one().literals())));
1098 }
1099 
1101  auto* mapping = m->GetOrCreate<CpModelMapping>();
1102  CHECK(!HasEnforcementLiteral(ct)) << "Not supported.";
1103  m->Add(LiteralXorIs(mapping->Literals(ct.bool_xor().literals()), true));
1104 }
1105 
1106 namespace {
1107 
1108 // Boolean encoding of:
1109 // enforcement_literal => coeff1 * var1 + coeff2 * var2 == rhs;
1110 void LoadEquivalenceAC(const std::vector<Literal> enforcement_literal,
1111  IntegerValue coeff1, IntegerVariable var1,
1112  IntegerValue coeff2, IntegerVariable var2,
1113  const IntegerValue rhs, Model* m) {
1114  auto* encoder = m->GetOrCreate<IntegerEncoder>();
1115  CHECK(encoder->VariableIsFullyEncoded(var1));
1116  CHECK(encoder->VariableIsFullyEncoded(var2));
1117  absl::flat_hash_map<IntegerValue, Literal> term1_value_to_literal;
1118  for (const auto value_literal : encoder->FullDomainEncoding(var1)) {
1119  term1_value_to_literal[coeff1 * value_literal.value] =
1120  value_literal.literal;
1121  }
1122  for (const auto value_literal : encoder->FullDomainEncoding(var2)) {
1123  const IntegerValue target = rhs - value_literal.value * coeff2;
1124  if (!gtl::ContainsKey(term1_value_to_literal, target)) {
1125  m->Add(EnforcedClause(enforcement_literal,
1126  {value_literal.literal.Negated()}));
1127  } else {
1128  const Literal target_literal = term1_value_to_literal[target];
1129  m->Add(EnforcedClause(enforcement_literal,
1130  {value_literal.literal.Negated(), target_literal}));
1131  m->Add(EnforcedClause(enforcement_literal,
1132  {value_literal.literal, target_literal.Negated()}));
1133 
1134  // This "target" can never be reached again, so it is safe to remove it.
1135  // We do that so we know the term1 values that are never reached.
1136  term1_value_to_literal.erase(target);
1137  }
1138  }
1139 
1140  // Exclude the values that can never be "matched" by coeff2 * var2.
1141  // We need the std::sort() to be deterministic!
1142  std::vector<Literal> implied_false;
1143  for (const auto entry : term1_value_to_literal) {
1144  implied_false.push_back(entry.second);
1145  }
1146  std::sort(implied_false.begin(), implied_false.end());
1147  for (const Literal l : implied_false) {
1148  m->Add(EnforcedClause(enforcement_literal, {l.Negated()}));
1149  }
1150 }
1151 
1152 // Boolean encoding of:
1153 // enforcement_literal => coeff1 * var1 + coeff2 * var2 != rhs;
1154 void LoadEquivalenceNeqAC(const std::vector<Literal> enforcement_literal,
1155  IntegerValue coeff1, IntegerVariable var1,
1156  IntegerValue coeff2, IntegerVariable var2,
1157  const IntegerValue rhs, Model* m) {
1158  auto* encoder = m->GetOrCreate<IntegerEncoder>();
1159  CHECK(encoder->VariableIsFullyEncoded(var1));
1160  CHECK(encoder->VariableIsFullyEncoded(var2));
1161  absl::flat_hash_map<IntegerValue, Literal> term1_value_to_literal;
1162  for (const auto value_literal : encoder->FullDomainEncoding(var1)) {
1163  term1_value_to_literal[coeff1 * value_literal.value] =
1164  value_literal.literal;
1165  }
1166  for (const auto value_literal : encoder->FullDomainEncoding(var2)) {
1167  const IntegerValue target_value = rhs - value_literal.value * coeff2;
1168  const auto& it = term1_value_to_literal.find(target_value);
1169  if (it != term1_value_to_literal.end()) {
1170  const Literal target_literal = it->second;
1171  m->Add(EnforcedClause(
1172  enforcement_literal,
1173  {value_literal.literal.Negated(), target_literal.Negated()}));
1174  }
1175  }
1176 }
1177 
1178 } // namespace
1179 
1181  auto* mapping = m->GetOrCreate<CpModelMapping>();
1182  if (ct.linear().vars().empty()) {
1183  const Domain rhs = ReadDomainFromProto(ct.linear());
1184  if (rhs.Contains(0)) return;
1185  if (HasEnforcementLiteral(ct)) {
1186  std::vector<Literal> clause;
1187  for (const int ref : ct.enforcement_literal()) {
1188  clause.push_back(mapping->Literal(ref).Negated());
1189  }
1190  m->Add(ClauseConstraint(clause));
1191  } else {
1192  VLOG(1) << "Trivially UNSAT constraint: " << ct.DebugString();
1193  m->GetOrCreate<SatSolver>()->NotifyThatModelIsUnsat();
1194  }
1195  return;
1196  }
1197 
1198  auto* integer_trail = m->GetOrCreate<IntegerTrail>();
1199  const std::vector<IntegerVariable> vars =
1200  mapping->Integers(ct.linear().vars());
1201  const std::vector<int64_t> coeffs = ValuesFromProto(ct.linear().coeffs());
1202 
1203  // Compute the min/max to relax the bounds if needed.
1204  //
1205  // TODO(user): Reuse ComputeLinearBounds()? but then we need another loop
1206  // to detect if we only have Booleans.
1207  IntegerValue min_sum(0);
1208  IntegerValue max_sum(0);
1209  IntegerValue max_domain_size(0);
1210  bool all_booleans = true;
1211  for (int i = 0; i < vars.size(); ++i) {
1212  if (all_booleans && !mapping->IsBoolean(ct.linear().vars(i))) {
1213  all_booleans = false;
1214  }
1215  const IntegerValue lb = integer_trail->LowerBound(vars[i]);
1216  const IntegerValue ub = integer_trail->UpperBound(vars[i]);
1217  max_domain_size = std::max(max_domain_size, ub - lb + 1);
1218  const IntegerValue term_a = coeffs[i] * lb;
1219  const IntegerValue term_b = coeffs[i] * ub;
1220  min_sum += std::min(term_a, term_b);
1221  max_sum += std::max(term_a, term_b);
1222  }
1223 
1224  const SatParameters& params = *m->GetOrCreate<SatParameters>();
1225  const IntegerValue domain_size_limit(
1227  if (ct.linear().vars_size() == 2 && !integer_trail->IsFixed(vars[0]) &&
1228  !integer_trail->IsFixed(vars[1]) &&
1229  max_domain_size <= domain_size_limit) {
1230  auto* encoder = m->GetOrCreate<IntegerEncoder>();
1231  if (params.boolean_encoding_level() > 0 && ConstraintIsEq(ct.linear()) &&
1232  ct.linear().domain(0) != min_sum && ct.linear().domain(0) != max_sum &&
1233  encoder->VariableIsFullyEncoded(vars[0]) &&
1234  encoder->VariableIsFullyEncoded(vars[1])) {
1235  VLOG(3) << "Load AC version of " << ct.DebugString() << ", var0 domain = "
1236  << integer_trail->InitialVariableDomain(vars[0])
1237  << ", var1 domain = "
1238  << integer_trail->InitialVariableDomain(vars[1]);
1239  return LoadEquivalenceAC(mapping->Literals(ct.enforcement_literal()),
1240  IntegerValue(coeffs[0]), vars[0],
1241  IntegerValue(coeffs[1]), vars[1],
1242  IntegerValue(ct.linear().domain(0)), m);
1243  }
1244 
1245  int64_t single_value = 0;
1246  if (params.boolean_encoding_level() > 0 &&
1247  ConstraintIsNEq(ct.linear(), mapping, integer_trail, &single_value) &&
1248  single_value != min_sum && single_value != max_sum &&
1249  encoder->VariableIsFullyEncoded(vars[0]) &&
1250  encoder->VariableIsFullyEncoded(vars[1])) {
1251  VLOG(3) << "Load NAC version of " << ct.DebugString()
1252  << ", var0 domain = "
1253  << integer_trail->InitialVariableDomain(vars[0])
1254  << ", var1 domain = "
1255  << integer_trail->InitialVariableDomain(vars[1])
1256  << ", value = " << single_value;
1257  return LoadEquivalenceNeqAC(mapping->Literals(ct.enforcement_literal()),
1258  IntegerValue(coeffs[0]), vars[0],
1259  IntegerValue(coeffs[1]), vars[1],
1260  IntegerValue(single_value), m);
1261  }
1262  }
1263 
1264  if (ct.linear().domain_size() == 2) {
1265  int64_t lb = ct.linear().domain(0);
1266  int64_t ub = ct.linear().domain(1);
1267  if (min_sum >= lb) lb = std::numeric_limits<int64_t>::min();
1268  if (max_sum <= ub) ub = std::numeric_limits<int64_t>::max();
1269 
1270  if (!HasEnforcementLiteral(ct)) {
1271  if (all_booleans) {
1272  // TODO(user): we should probably also implement an
1273  // half-reified version of this constraint.
1274  std::vector<LiteralWithCoeff> cst;
1275  for (int i = 0; i < vars.size(); ++i) {
1276  const int ref = ct.linear().vars(i);
1277  cst.push_back({mapping->Literal(ref), coeffs[i]});
1278  }
1279  m->Add(BooleanLinearConstraint(lb, ub, &cst));
1280  } else {
1281  if (lb != std::numeric_limits<int64_t>::min()) {
1282  m->Add(WeightedSumGreaterOrEqual(vars, coeffs, lb));
1283  }
1284  if (ub != std::numeric_limits<int64_t>::max()) {
1285  m->Add(WeightedSumLowerOrEqual(vars, coeffs, ub));
1286  }
1287  }
1288  } else {
1289  const std::vector<Literal> enforcement_literals =
1290  mapping->Literals(ct.enforcement_literal());
1291  if (lb != std::numeric_limits<int64_t>::min()) {
1292  m->Add(ConditionalWeightedSumGreaterOrEqual(enforcement_literals, vars,
1293  coeffs, lb));
1294  }
1295  if (ub != std::numeric_limits<int64_t>::max()) {
1296  m->Add(ConditionalWeightedSumLowerOrEqual(enforcement_literals, vars,
1297  coeffs, ub));
1298  }
1299  }
1300  } else {
1301  // In this case, we can create just one Boolean instead of two since one
1302  // is the negation of the other.
1303  const bool special_case =
1304  ct.enforcement_literal().empty() && ct.linear().domain_size() == 4;
1305 
1306  std::vector<Literal> clause;
1307  for (int i = 0; i < ct.linear().domain_size(); i += 2) {
1308  int64_t lb = ct.linear().domain(i);
1309  int64_t ub = ct.linear().domain(i + 1);
1310  if (min_sum >= lb) lb = std::numeric_limits<int64_t>::min();
1311  if (max_sum <= ub) ub = std::numeric_limits<int64_t>::max();
1312 
1313  const Literal subdomain_literal(
1314  special_case && i > 0 ? clause.back().Negated()
1315  : Literal(m->Add(NewBooleanVariable()), true));
1316  clause.push_back(subdomain_literal);
1317 
1318  if (lb != std::numeric_limits<int64_t>::min()) {
1319  m->Add(ConditionalWeightedSumGreaterOrEqual({subdomain_literal}, vars,
1320  coeffs, lb));
1321  }
1322  if (ub != std::numeric_limits<int64_t>::max()) {
1323  m->Add(ConditionalWeightedSumLowerOrEqual({subdomain_literal}, vars,
1324  coeffs, ub));
1325  }
1326  }
1327  for (const int ref : ct.enforcement_literal()) {
1328  clause.push_back(mapping->Literal(ref).Negated());
1329  }
1330  if (!special_case) m->Add(ClauseConstraint(clause));
1331  }
1332 }
1333 
1335  auto* mapping = m->GetOrCreate<CpModelMapping>();
1336  const std::vector<IntegerVariable> vars =
1337  mapping->Integers(ct.all_diff().vars());
1338  // If all variables are fully encoded and domains are not too large, use
1339  // arc-consistent reasoning. Otherwise, use bounds-consistent reasoning.
1340  IntegerTrail* integer_trail = m->GetOrCreate<IntegerTrail>();
1341  IntegerEncoder* encoder = m->GetOrCreate<IntegerEncoder>();
1342  int num_fully_encoded = 0;
1343  int64_t max_domain_size = 0;
1344  for (const IntegerVariable variable : vars) {
1345  if (encoder->VariableIsFullyEncoded(variable)) num_fully_encoded++;
1346 
1347  IntegerValue lb = integer_trail->LowerBound(variable);
1348  IntegerValue ub = integer_trail->UpperBound(variable);
1349  const int64_t domain_size = ub.value() - lb.value() + 1;
1350  max_domain_size = std::max(max_domain_size, domain_size);
1351  }
1352 
1353  if (num_fully_encoded == vars.size() && max_domain_size < 1024) {
1354  m->Add(AllDifferentBinary(vars));
1355  m->Add(AllDifferentAC(vars));
1356  } else {
1357  m->Add(AllDifferentOnBounds(vars));
1358  }
1359 }
1360 
1362  auto* mapping = m->GetOrCreate<CpModelMapping>();
1363  const IntegerVariable prod = mapping->Integer(ct.int_prod().target());
1364  const std::vector<IntegerVariable> vars =
1365  mapping->Integers(ct.int_prod().vars());
1366  CHECK_EQ(vars.size(), 2) << "General int_prod not supported yet.";
1367  m->Add(ProductConstraint(vars[0], vars[1], prod));
1368 }
1369 
1371  auto* mapping = m->GetOrCreate<CpModelMapping>();
1372  const IntegerVariable div = mapping->Integer(ct.int_div().target());
1373  const std::vector<IntegerVariable> vars =
1374  mapping->Integers(ct.int_div().vars());
1375  if (m->Get(IsFixed(vars[1]))) {
1376  const IntegerValue denom(m->Get(Value(vars[1])));
1377  if (denom == 1) {
1378  m->Add(Equality(vars[0], div));
1379  } else {
1380  m->Add(FixedDivisionConstraint(vars[0], denom, div));
1381  }
1382  } else {
1383  m->Add(DivisionConstraint(vars[0], vars[1], div));
1384  }
1385 }
1386 
1388  auto* mapping = m->GetOrCreate<CpModelMapping>();
1389  const IntegerVariable min = mapping->Integer(ct.int_min().target());
1390  const std::vector<IntegerVariable> vars =
1391  mapping->Integers(ct.int_min().vars());
1392  m->Add(IsEqualToMinOf(min, vars));
1393 }
1394 
1396  if (ct.lin_max().exprs().empty()) {
1397  m->GetOrCreate<SatSolver>()->NotifyThatModelIsUnsat();
1398  return;
1399  }
1400 
1401  auto* mapping = m->GetOrCreate<CpModelMapping>();
1402  const LinearExpression max = mapping->GetExprFromProto(ct.lin_max().target());
1403  std::vector<LinearExpression> negated_exprs;
1404  negated_exprs.reserve(ct.lin_max().exprs_size());
1405  for (int i = 0; i < ct.lin_max().exprs_size(); ++i) {
1406  negated_exprs.push_back(
1407  NegationOf(mapping->GetExprFromProto(ct.lin_max().exprs(i))));
1408  }
1409  // TODO(user): Consider replacing the min propagator by max.
1410  m->Add(IsEqualToMinOf(NegationOf(max), negated_exprs));
1411 }
1412 
1414  auto* mapping = m->GetOrCreate<CpModelMapping>();
1415  const IntegerVariable max = mapping->Integer(ct.int_max().target());
1416  const std::vector<IntegerVariable> vars =
1417  mapping->Integers(ct.int_max().vars());
1418  m->Add(IsEqualToMaxOf(max, vars));
1419 }
1420 
1422  auto* mapping = m->GetOrCreate<CpModelMapping>();
1423  m->Add(Disjunctive(mapping->Intervals(ct.no_overlap().intervals())));
1424 }
1425 
1427  if (ct.no_overlap_2d().x_intervals().empty()) return;
1428  auto* mapping = m->GetOrCreate<CpModelMapping>();
1429  const std::vector<IntervalVariable> x_intervals =
1430  mapping->Intervals(ct.no_overlap_2d().x_intervals());
1431  const std::vector<IntervalVariable> y_intervals =
1432  mapping->Intervals(ct.no_overlap_2d().y_intervals());
1434  x_intervals, y_intervals,
1435  !ct.no_overlap_2d().boxes_with_null_area_can_overlap()));
1436 }
1437 
1439  auto* mapping = m->GetOrCreate<CpModelMapping>();
1440  const std::vector<IntervalVariable> intervals =
1441  mapping->Intervals(ct.cumulative().intervals());
1442  const AffineExpression capacity(mapping->Integer(ct.cumulative().capacity()));
1443  std::vector<AffineExpression> demands;
1444  for (const IntegerVariable var :
1445  mapping->Integers(ct.cumulative().demands())) {
1446  demands.push_back(AffineExpression(var));
1447  }
1448  m->Add(Cumulative(intervals, demands, capacity));
1449 }
1450 
1452  auto* mapping = m->GetOrCreate<CpModelMapping>();
1453  auto* encoder = m->GetOrCreate<IntegerEncoder>();
1454  std::vector<AffineExpression> times;
1455  std::vector<IntegerValue> deltas;
1456  std::vector<Literal> presences;
1457  const int size = ct.reservoir().times().size();
1458  for (int i = 0; i < size; ++i) {
1459  times.push_back(mapping->Integer(ct.reservoir().times(i)));
1460  deltas.push_back(IntegerValue(ct.reservoir().demands(i)));
1461  if (!ct.reservoir().actives().empty()) {
1462  presences.push_back(mapping->Literal(ct.reservoir().actives(i)));
1463  } else {
1464  presences.push_back(encoder->GetTrueLiteral());
1465  }
1466  }
1467  AddReservoirConstraint(times, deltas, presences, ct.reservoir().min_level(),
1468  ct.reservoir().max_level(), m);
1469 }
1470 
1471 // If a variable is constant and its value appear in no other variable domains,
1472 // then the literal encoding the index and the one encoding the target at this
1473 // value are equivalent.
1475  Model* m) {
1476  auto* mapping = m->GetOrCreate<CpModelMapping>();
1477  IntegerEncoder* encoder = m->GetOrCreate<IntegerEncoder>();
1478  IntegerTrail* integer_trail = m->GetOrCreate<IntegerTrail>();
1479 
1480  const IntegerVariable index = mapping->Integer(ct.element().index());
1481  const IntegerVariable target = mapping->Integer(ct.element().target());
1482  const std::vector<IntegerVariable> vars =
1483  mapping->Integers(ct.element().vars());
1484  CHECK(!m->Get(IsFixed(index)));
1485  CHECK(!m->Get(IsFixed(target)));
1486 
1487  Domain union_of_non_constant_domains;
1488  std::map<IntegerValue, int> constant_to_num;
1489  for (const auto literal_value : m->Add(FullyEncodeVariable(index))) {
1490  const int i = literal_value.value.value();
1491  if (m->Get(IsFixed(vars[i]))) {
1492  const IntegerValue value(m->Get(Value(vars[i])));
1493  constant_to_num[value]++;
1494  } else {
1495  union_of_non_constant_domains = union_of_non_constant_domains.UnionWith(
1496  integer_trail->InitialVariableDomain(vars[i]));
1497  }
1498  }
1499 
1500  // Bump the number if the constant appear in union_of_non_constant_domains.
1501  for (const auto entry : constant_to_num) {
1502  if (union_of_non_constant_domains.Contains(entry.first.value())) {
1503  constant_to_num[entry.first]++;
1504  }
1505  }
1506 
1507  // Use the literal from the index encoding to encode the target at the
1508  // "unique" values.
1509  bool is_one_to_one_mapping = true;
1510  for (const auto literal_value : m->Add(FullyEncodeVariable(index))) {
1511  const int i = literal_value.value.value();
1512  if (!m->Get(IsFixed(vars[i]))) {
1513  is_one_to_one_mapping = false;
1514  continue;
1515  }
1516 
1517  const IntegerValue value(m->Get(Value(vars[i])));
1518  if (constant_to_num[value] == 1) {
1519  const Literal r = literal_value.literal;
1520  encoder->AssociateToIntegerEqualValue(r, target, value);
1521  } else {
1522  is_one_to_one_mapping = false;
1523  }
1524  }
1525 
1526  return is_one_to_one_mapping;
1527 }
1528 
1529 // TODO(user): Be more efficient when the element().vars() are constants.
1530 // Ideally we should avoid creating them as integer variable since we don't
1531 // use them.
1533  auto* mapping = m->GetOrCreate<CpModelMapping>();
1534  const IntegerVariable index = mapping->Integer(ct.element().index());
1535  const IntegerVariable target = mapping->Integer(ct.element().target());
1536  const std::vector<IntegerVariable> vars =
1537  mapping->Integers(ct.element().vars());
1538  CHECK(!m->Get(IsFixed(index)));
1539 
1540  // We always fully encode the index on an element constraint.
1541  const auto encoding = m->Add(FullyEncodeVariable((index)));
1542  std::vector<Literal> selectors;
1543  std::vector<IntegerVariable> possible_vars;
1544  for (const auto literal_value : encoding) {
1545  const int i = literal_value.value.value();
1546  CHECK_GE(i, 0);
1547  CHECK_LT(i, vars.size());
1548  possible_vars.push_back(vars[i]);
1549  selectors.push_back(literal_value.literal);
1550  const Literal r = literal_value.literal;
1551 
1552  if (vars[i] == target) continue;
1553  if (m->Get(IsFixed(target))) {
1554  const int64_t value = m->Get(Value(target));
1555  m->Add(ImpliesInInterval(r, vars[i], value, value));
1556  } else if (m->Get(IsFixed(vars[i]))) {
1557  const int64_t value = m->Get(Value(vars[i]));
1558  m->Add(ImpliesInInterval(r, target, value, value));
1559  } else {
1560  m->Add(ConditionalLowerOrEqualWithOffset(vars[i], target, 0, r));
1561  m->Add(ConditionalLowerOrEqualWithOffset(target, vars[i], 0, r));
1562  }
1563  }
1564 
1565  if (!m->Get(IsFixed(target))) {
1566  m->Add(PartialIsOneOfVar(target, possible_vars, selectors));
1567  }
1568 }
1569 
1570 // Arc-Consistent encoding of the element constraint as SAT clauses.
1571 // The constraint enforces vars[index] == target.
1572 //
1573 // The AC propagation can be decomposed in three rules:
1574 // Rule 1: dom(index) == i => dom(vars[i]) == dom(target).
1575 // Rule 2: dom(target) \subseteq \Union_{i \in dom(index)} dom(vars[i]).
1576 // Rule 3: dom(index) \subseteq { i | |dom(vars[i]) \inter dom(target)| > 0 }.
1577 //
1578 // We encode this in a way similar to the table constraint, except that the
1579 // set of admissible tuples is not explicit.
1580 // First, we add Booleans selected[i][value] <=> (index == i /\ vars[i] ==
1581 // value). Rules 1 and 2 are enforced by target == value <=> \Or_{i}
1582 // selected[i][value]. Rule 3 is enforced by index == i <=> \Or_{value}
1583 // selected[i][value].
1585  auto* mapping = m->GetOrCreate<CpModelMapping>();
1586  const IntegerVariable index = mapping->Integer(ct.element().index());
1587  const IntegerVariable target = mapping->Integer(ct.element().target());
1588  const std::vector<IntegerVariable> vars =
1589  mapping->Integers(ct.element().vars());
1590  CHECK(!m->Get(IsFixed(index)));
1591  CHECK(!m->Get(IsFixed(target)));
1592 
1593  absl::flat_hash_map<IntegerValue, Literal> target_map;
1594  const auto target_encoding = m->Add(FullyEncodeVariable(target));
1595  for (const auto literal_value : target_encoding) {
1596  target_map[literal_value.value] = literal_value.literal;
1597  }
1598 
1599  // For i \in index and value in vars[i], make (index == i /\ vars[i] == value)
1600  // literals and store them by value in vectors.
1601  absl::flat_hash_map<IntegerValue, std::vector<Literal>> value_to_literals;
1602  const auto index_encoding = m->Add(FullyEncodeVariable(index));
1603  IntegerTrail* integer_trail = m->GetOrCreate<IntegerTrail>();
1604  for (const auto literal_value : index_encoding) {
1605  const int i = literal_value.value.value();
1606  const Literal i_lit = literal_value.literal;
1607 
1608  // Special case where vars[i] == value /\ i_lit is actually i_lit.
1609  if (m->Get(IsFixed(vars[i]))) {
1610  value_to_literals[integer_trail->LowerBound(vars[i])].push_back(i_lit);
1611  continue;
1612  }
1613 
1614  const auto var_encoding = m->Add(FullyEncodeVariable(vars[i]));
1615  std::vector<Literal> var_selected_literals;
1616  for (const auto var_literal_value : var_encoding) {
1617  const IntegerValue value = var_literal_value.value;
1618  const Literal var_is_value = var_literal_value.literal;
1619 
1620  if (!gtl::ContainsKey(target_map, value)) {
1621  // No need to add to value_to_literals, selected[i][value] is always
1622  // false.
1623  m->Add(Implication(i_lit, var_is_value.Negated()));
1624  continue;
1625  }
1626 
1627  const Literal var_is_value_and_selected =
1628  Literal(m->Add(NewBooleanVariable()), true);
1629  m->Add(ReifiedBoolAnd({i_lit, var_is_value}, var_is_value_and_selected));
1630  value_to_literals[value].push_back(var_is_value_and_selected);
1631  var_selected_literals.push_back(var_is_value_and_selected);
1632  }
1633  // index == i <=> \Or_{value} selected[i][value].
1634  m->Add(ReifiedBoolOr(var_selected_literals, i_lit));
1635  }
1636 
1637  // target == value <=> \Or_{i \in index} (vars[i] == value /\ index == i).
1638  for (const auto& entry : target_map) {
1639  const IntegerValue value = entry.first;
1640  const Literal target_is_value = entry.second;
1641 
1642  if (!gtl::ContainsKey(value_to_literals, value)) {
1643  m->Add(ClauseConstraint({target_is_value.Negated()}));
1644  } else {
1645  m->Add(ReifiedBoolOr(value_to_literals[value], target_is_value));
1646  }
1647  }
1648 }
1649 
1650 namespace {
1651 
1652 // This Boolean encoding is enough for consistency, but does not propagate as
1653 // much as LoadElementConstraintAC(). However, setting any of the non-propagated
1654 // Booleans to its "wrong" value will result directly in a conflict, so the
1655 // solver will easily learn an AC encoding...
1656 //
1657 // The advantage is that this does not introduce extra BooleanVariables.
1658 void LoadElementConstraintHalfAC(const ConstraintProto& ct, Model* m) {
1659  auto* mapping = m->GetOrCreate<CpModelMapping>();
1660  const IntegerVariable index = mapping->Integer(ct.element().index());
1661  const IntegerVariable target = mapping->Integer(ct.element().target());
1662  const std::vector<IntegerVariable> vars =
1663  mapping->Integers(ct.element().vars());
1664  CHECK(!m->Get(IsFixed(index)));
1665  CHECK(!m->Get(IsFixed(target)));
1666 
1667  m->Add(FullyEncodeVariable(target));
1668  for (const auto value_literal : m->Add(FullyEncodeVariable(index))) {
1669  const int i = value_literal.value.value();
1670  m->Add(FullyEncodeVariable(vars[i]));
1671  LoadEquivalenceAC({value_literal.literal}, IntegerValue(1), vars[i],
1672  IntegerValue(-1), target, IntegerValue(0), m);
1673  }
1674 }
1675 
1676 void LoadBooleanElement(const ConstraintProto& ct, Model* m) {
1677  auto* mapping = m->GetOrCreate<CpModelMapping>();
1678  const IntegerVariable index = mapping->Integer(ct.element().index());
1679  const std::vector<Literal> literals = mapping->Literals(ct.element().vars());
1680  const Literal target = mapping->Literal(ct.element().target());
1681 
1682  if (m->Get(IsFixed(index))) {
1683  m->Add(Equality(target, literals[m->Get(Value(index))]));
1684  return;
1685  }
1686 
1687  std::vector<Literal> all_true;
1688  std::vector<Literal> all_false;
1689  for (const auto value_literal : m->Add(FullyEncodeVariable(index))) {
1690  const Literal a_lit = literals[value_literal.value.value()];
1691  const Literal i_lit = value_literal.literal;
1692  m->Add(ClauseConstraint({i_lit.Negated(), a_lit.Negated(), target}));
1693  m->Add(ClauseConstraint({i_lit.Negated(), a_lit, target.Negated()}));
1694  all_true.push_back(a_lit.Negated());
1695  all_false.push_back(a_lit);
1696  }
1697  all_true.push_back(target);
1698  all_false.push_back(target.Negated());
1699  m->Add(ClauseConstraint(all_true));
1700  m->Add(ClauseConstraint(all_false));
1701  // TODO(user): Investigate filtering this with active literals.
1702 }
1703 
1704 } // namespace
1705 
1707  auto* mapping = m->GetOrCreate<CpModelMapping>();
1708  const IntegerVariable index = mapping->Integer(ct.element().index());
1709 
1710  bool boolean_array = true;
1711  for (const int ref : ct.element().vars()) {
1712  if (!mapping->IsBoolean(ref)) {
1713  boolean_array = false;
1714  break;
1715  }
1716  }
1717  if (boolean_array && !mapping->IsBoolean(ct.element().target())) {
1718  // Should have been reduced but presolve.
1719  VLOG(1) << "Fix boolean_element not propagated on target";
1720  boolean_array = false;
1721  }
1722 
1723  // TODO(user): Move this to presolve. Leads to a larger discussion on
1724  // adding full encoding to model during presolve.
1725  if (boolean_array) {
1726  LoadBooleanElement(ct, m);
1727  return;
1728  }
1729 
1730  const IntegerVariable target = mapping->Integer(ct.element().target());
1731  const std::vector<IntegerVariable> vars =
1732  mapping->Integers(ct.element().vars());
1733 
1734  // Retrict the domain of index in case there was no presolve.
1736  index, Domain(0, vars.size() - 1))) {
1737  return;
1738  }
1739 
1740  // This returns true if there is nothing else to do after the equivalences
1741  // of the form (index literal <=> target_literal) have been added.
1742  if (!m->Get(IsFixed(index)) && !m->Get(IsFixed(target)) &&
1744  return;
1745  }
1746 
1747  // Special case when index is fixed.
1748  if (m->Get(IsFixed(index))) {
1749  m->Add(Equality(target, vars[m->Get(Value(index))]));
1750  return;
1751  }
1752 
1753  // Special case when target is fixed.
1754  if (m->Get(IsFixed(target))) {
1755  return LoadElementConstraintBounds(ct, m);
1756  }
1757 
1758  IntegerEncoder* encoder = m->GetOrCreate<IntegerEncoder>();
1759  const bool target_is_AC = encoder->VariableIsFullyEncoded(target);
1760 
1761  int num_AC_variables = 0;
1762  const int num_vars = ct.element().vars().size();
1763  for (const int v : ct.element().vars()) {
1764  IntegerVariable variable = mapping->Integer(v);
1765  const bool is_full =
1766  m->Get(IsFixed(variable)) || encoder->VariableIsFullyEncoded(variable);
1767  if (is_full) num_AC_variables++;
1768  }
1769 
1770  const SatParameters& params = *m->GetOrCreate<SatParameters>();
1771  if (params.boolean_encoding_level() > 0 &&
1772  (target_is_AC || num_AC_variables >= num_vars - 1)) {
1773  if (params.boolean_encoding_level() > 1) {
1775  } else {
1776  LoadElementConstraintHalfAC(ct, m);
1777  }
1778  } else {
1780  }
1781 }
1782 
1784  auto* mapping = m->GetOrCreate<CpModelMapping>();
1785  const std::vector<IntegerVariable> vars =
1786  mapping->Integers(ct.table().vars());
1787  const std::vector<int64_t> values = ValuesFromProto(ct.table().values());
1788  const int num_vars = vars.size();
1789  const int num_tuples = values.size() / num_vars;
1790  std::vector<std::vector<int64_t>> tuples(num_tuples);
1791  int count = 0;
1792  for (int i = 0; i < num_tuples; ++i) {
1793  for (int j = 0; j < num_vars; ++j) {
1794  tuples[i].push_back(values[count++]);
1795  }
1796  }
1797  if (ct.table().negated()) {
1798  AddNegatedTableConstraint(vars, std::move(tuples), m);
1799  } else {
1800  AddTableConstraint(vars, std::move(tuples), m);
1801  }
1802 }
1803 
1805  auto* mapping = m->GetOrCreate<CpModelMapping>();
1806  const std::vector<IntegerVariable> vars =
1807  mapping->Integers(ct.automaton().vars());
1808 
1809  const int num_transitions = ct.automaton().transition_tail_size();
1810  std::vector<std::vector<int64_t>> transitions;
1811  transitions.reserve(num_transitions);
1812  for (int i = 0; i < num_transitions; ++i) {
1813  transitions.push_back({ct.automaton().transition_tail(i),
1814  ct.automaton().transition_label(i),
1815  ct.automaton().transition_head(i)});
1816  }
1817 
1818  const int64_t starting_state = ct.automaton().starting_state();
1819  const std::vector<int64_t> final_states =
1820  ValuesFromProto(ct.automaton().final_states());
1821  m->Add(TransitionConstraint(vars, transitions, starting_state, final_states));
1822 }
1823 
1824 // From vector of n IntegerVariables, returns an n x n matrix of Literal
1825 // such that matrix[i][j] is the Literal corresponding to vars[i] == j.
1826 std::vector<std::vector<Literal>> GetSquareMatrixFromIntegerVariables(
1827  const std::vector<IntegerVariable>& vars, Model* m) {
1828  const int n = vars.size();
1829  const Literal kTrueLiteral =
1830  m->GetOrCreate<IntegerEncoder>()->GetTrueLiteral();
1831  const Literal kFalseLiteral =
1832  m->GetOrCreate<IntegerEncoder>()->GetFalseLiteral();
1833  std::vector<std::vector<Literal>> matrix(
1834  n, std::vector<Literal>(n, kFalseLiteral));
1835  for (int i = 0; i < n; i++) {
1836  for (int j = 0; j < n; j++) {
1837  if (m->Get(IsFixed(vars[i]))) {
1838  const int value = m->Get(Value(vars[i]));
1839  DCHECK_LE(0, value);
1840  DCHECK_LT(value, n);
1841  matrix[i][value] = kTrueLiteral;
1842  } else {
1843  const auto encoding = m->Add(FullyEncodeVariable(vars[i]));
1844  for (const auto& entry : encoding) {
1845  const int value = entry.value.value();
1846  DCHECK_LE(0, value);
1847  DCHECK_LT(value, n);
1848  matrix[i][value] = entry.literal;
1849  }
1850  }
1851  }
1852  }
1853  return matrix;
1854 }
1855 
1857  const auto& circuit = ct.circuit();
1858  if (circuit.tails().empty()) return;
1859 
1860  std::vector<int> tails(circuit.tails().begin(), circuit.tails().end());
1861  std::vector<int> heads(circuit.heads().begin(), circuit.heads().end());
1862  std::vector<Literal> literals =
1863  m->GetOrCreate<CpModelMapping>()->Literals(circuit.literals());
1864  const int num_nodes = ReindexArcs(&tails, &heads);
1865  m->Add(SubcircuitConstraint(num_nodes, tails, heads, literals));
1866 }
1867 
1869  const auto& routes = ct.routes();
1870  if (routes.tails().empty()) return;
1871 
1872  std::vector<int> tails(routes.tails().begin(), routes.tails().end());
1873  std::vector<int> heads(routes.heads().begin(), routes.heads().end());
1874  std::vector<Literal> literals =
1875  m->GetOrCreate<CpModelMapping>()->Literals(routes.literals());
1876  const int num_nodes = ReindexArcs(&tails, &heads);
1877  m->Add(SubcircuitConstraint(num_nodes, tails, heads, literals,
1878  /*multiple_subcircuit_through_zero=*/true));
1879 }
1880 
1882  switch (ct.constraint_case()) {
1883  case ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET:
1884  return true;
1885  case ConstraintProto::ConstraintCase::kBoolOr:
1887  return true;
1888  case ConstraintProto::ConstraintCase::kBoolAnd:
1890  return true;
1891  case ConstraintProto::ConstraintCase::kAtMostOne:
1893  return true;
1894  case ConstraintProto::ConstraintCase::kExactlyOne:
1896  return true;
1897  case ConstraintProto::ConstraintCase::kBoolXor:
1899  return true;
1900  case ConstraintProto::ConstraintProto::kLinear:
1902  return true;
1903  case ConstraintProto::ConstraintProto::kAllDiff:
1905  return true;
1906  case ConstraintProto::ConstraintProto::kIntProd:
1908  return true;
1909  case ConstraintProto::ConstraintProto::kIntDiv:
1911  return true;
1912  case ConstraintProto::ConstraintProto::kIntMin:
1914  return true;
1915  case ConstraintProto::ConstraintProto::kLinMax:
1917  return true;
1918  case ConstraintProto::ConstraintProto::kIntMax:
1920  return true;
1921  case ConstraintProto::ConstraintProto::kInterval:
1922  // Already dealt with.
1923  return true;
1924  case ConstraintProto::ConstraintProto::kNoOverlap:
1926  return true;
1927  case ConstraintProto::ConstraintProto::kNoOverlap2D:
1929  return true;
1930  case ConstraintProto::ConstraintProto::kCumulative:
1932  return true;
1933  case ConstraintProto::ConstraintProto::kReservoir:
1935  return true;
1936  case ConstraintProto::ConstraintProto::kElement:
1938  return true;
1939  case ConstraintProto::ConstraintProto::kTable:
1940  LoadTableConstraint(ct, m);
1941  return true;
1942  case ConstraintProto::ConstraintProto::kAutomaton:
1944  return true;
1945  case ConstraintProto::ConstraintProto::kCircuit:
1947  return true;
1948  case ConstraintProto::ConstraintProto::kRoutes:
1950  return true;
1951  default:
1952  return false;
1953  }
1954 }
1955 
1956 } // namespace sat
1957 } // namespace operations_research
std::vector< int > UsedVariables(const ConstraintProto &ct)
void AddNegatedTableConstraint(absl::Span< const IntegerVariable > vars, std::vector< std::vector< int64_t >> tuples, Model *model)
Definition: sat/table.cc:460
void DetectOptionalVariables(const CpModelProto &model_proto, Model *m)
#define CHECK(condition)
Definition: base/logging.h:491
std::function< void(Model *)> AllDifferentBinary(const std::vector< IntegerVariable > &vars)
Definition: all_different.h:35
void LoadIntMinConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> ProductConstraint(IntegerVariable a, IntegerVariable b, IntegerVariable p)
Definition: integer_expr.h:772
std::function< void(Model *)> FixedDivisionConstraint(IntegerVariable a, IntegerValue b, IntegerVariable c)
Definition: integer_expr.h:826
std::function< BooleanVariable(Model *)> NewBooleanVariable()
Definition: integer.h:1469
std::function< void(Model *)> WeightedSumLowerOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
Definition: integer_expr.h:300
int64_t min
Definition: alldiff_cst.cc:139
std::vector< sat::Literal > Literals(const ProtoIndices &indices) const
void LoadReservoirConstraint(const ConstraintProto &ct, Model *m)
#define SOLVER_LOG(logger,...)
Definition: util/logging.h:63
#define CHECK_GE(val1, val2)
Definition: base/logging.h:702
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
Literal(int signed_value)
Definition: sat_base.h:69
void LoadIntDivConstraint(const ConstraintProto &ct, Model *m)
static constexpr DomainReductionStrategy SELECT_MEDIAN_VALUE
Definition: cp_model.pb.h:5166
bool VariableIsFullyEncoded(IntegerVariable var) const
Definition: integer.cc:97
std::function< void(Model *)> ReifiedBoolOr(const std::vector< Literal > &literals, Literal r)
Definition: sat_solver.h:936
void LoadLinMaxConstraint(const ConstraintProto &ct, Model *m)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1315
std::function< void(Model *)> ReifiedBoolAnd(const std::vector< Literal > &literals, Literal r)
Definition: sat_solver.h:970
#define VLOG(verboselevel)
Definition: base/logging.h:979
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
Definition: sat/model.h:106
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1345
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:263
std::function< void(Model *)> ConditionalWeightedSumGreaterOrEqual(const std::vector< Literal > &enforcement_literals, const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
Definition: integer_expr.h:517
LiteralIndex Index() const
Definition: sat_base.h:85
void LoadTableConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> TransitionConstraint(const std::vector< IntegerVariable > &vars, const std::vector< std::vector< int64_t >> &automaton, int64_t initial_state, const std::vector< int64_t > &final_states)
Definition: table.h:64
GRBmodel * model
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition: stl_util.h:58
void ExtractEncoding(const CpModelProto &model_proto, Model *m)
bool LoadConstraint(const ConstraintProto &ct, Model *m)
int64_t Max() const
Returns the max value of the domain.
void LoadRoutesConstraint(const ConstraintProto &ct, Model *m)
void LoadAtMostOneConstraint(const ConstraintProto &ct, Model *m)
void LoadAutomatonConstraint(const ConstraintProto &ct, Model *m)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
Definition: integer.h:1544
#define CHECK_LT(val1, val2)
Definition: base/logging.h:701
void LoadCumulativeConstraint(const ConstraintProto &ct, Model *m)
::PROTOBUF_NAMESPACE_ID::int32 vars(int index) const
std::function< void(Model *)> IsEqualToMaxOf(IntegerVariable max_var, const std::vector< IntegerVariable > &vars)
Definition: integer_expr.h:744
const ::operations_research::sat::SymmetryProto & symmetry() const
int ReindexArcs(IntContainer *tails, IntContainer *heads)
Definition: circuit.h:168
int64_t max
Definition: alldiff_cst.cc:140
std::function< void(Model *)> IsEqualToMinOf(IntegerVariable min_var, const std::vector< IntegerVariable > &vars)
Definition: integer_expr.h:675
void LoadElementConstraintAC(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> AtMostOneConstraint(const std::vector< Literal > &literals)
Definition: sat_solver.h:892
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
CpModelProto proto
int64_t Min() const
Returns the min value of the domain.
::PROTOBUF_NAMESPACE_ID::int32 boolean_encoding_level() const
std::function< IntervalVariable(Model *)> NewInterval(int64_t min_start, int64_t max_end, int64_t size)
Definition: intervals.h:666
void MaybeFullyEncodeMoreVariables(const CpModelProto &model_proto, Model *m)
T Get(std::function< T(const Model &)> f) const
Similar to Add() but this is const.
Definition: sat/model.h:87
const BooleanVariable kNoBooleanVariable(-1)
std::function< void(Model *)> BooleanLinearConstraint(int64_t lower_bound, int64_t upper_bound, std::vector< LiteralWithCoeff > *cst)
Definition: sat_solver.h:853
std::function< void(Model *)> DivisionConstraint(IntegerVariable a, IntegerVariable b, IntegerVariable c)
Definition: integer_expr.h:813
std::function< void(Model *)> ImpliesInInterval(Literal in_interval, IntegerVariable v, int64_t lb, int64_t ub)
Definition: integer.h:1622
void LoadNoOverlap2dConstraint(const ConstraintProto &ct, Model *m)
const ::operations_research::sat::ConstraintProto & constraints(int index) const
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:200
std::function< bool(const Model &)> IsFixed(IntegerVariable v)
Definition: integer.h:1536
void LoadNoOverlapConstraint(const ConstraintProto &ct, Model *m)
const ::operations_research::sat::CpObjectiveProto & objective() const
bool UpdateInitialDomain(IntegerVariable var, Domain domain)
Definition: integer.cc:686
std::function< void(Model *)> Disjunctive(const std::vector< IntervalVariable > &vars)
Definition: disjunctive.h:39
int64_t capacity
void LoadExactlyOneConstraint(const ConstraintProto &ct, Model *m)
std::function< std::vector< IntegerEncoder::ValueLiteralPair >Model *)> FullyEncodeVariable(IntegerVariable var)
Definition: integer.h:1645
int index
Definition: pack.cc:509
std::function< IntervalVariable(Model *)> NewOptionalInterval(int64_t min_start, int64_t max_end, int64_t size, Literal is_present)
Definition: intervals.h:696
int64_t Size() const
Returns the number of elements in the domain.
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
std::function< void(Model *)> EnforcedClause(absl::Span< const Literal > enforcement_literals, absl::Span< const Literal > clause)
Definition: sat_solver.h:952
std::function< void(Model *)> ConditionalWeightedSumLowerOrEqual(const std::vector< Literal > &enforcement_literals, const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
Definition: integer_expr.h:429
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
std::function< void(Model *)> ConditionalLowerOrEqualWithOffset(IntegerVariable a, IntegerVariable b, int64_t offset, Literal is_le)
Definition: precedences.h:420
std::vector< std::vector< Literal > > GetSquareMatrixFromIntegerVariables(const std::vector< IntegerVariable > &vars, Model *m)
CpModelProto const * model_proto
void LoadIntProdConstraint(const ConstraintProto &ct, Model *m)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:29
std::function< void(Model *)> Implication(const std::vector< Literal > &enforcement_literals, IntegerLiteral i)
Definition: integer.h:1595
void LoadCircuitConstraint(const ConstraintProto &ct, Model *m)
We call domain any subset of Int64 = [kint64min, kint64max].
std::function< void(Model *)> SubcircuitConstraint(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, bool multiple_subcircuit_through_zero=false)
Definition: circuit.h:201
void LoadElementConstraintBounds(const ConstraintProto &ct, Model *m)
void AddFullEncodingFromSearchBranching(const CpModelProto &model_proto, Model *m)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:91
std::function< void(Model *)> PartialIsOneOfVar(IntegerVariable target_var, const std::vector< IntegerVariable > &vars, const std::vector< Literal > &selectors)
T Add(std::function< T(Model *)> f)
This makes it possible to have a nicer API on the client side, and it allows both of these forms:
Definition: sat/model.h:81
bool Contains(int64_t value) const
Returns true iff value is in Domain.
bool HasEnforcementLiteral(const ConstraintProto &ct)
void PropagateEncodingFromEquivalenceRelations(const CpModelProto &model_proto, Model *m)
const ::operations_research::sat::DecisionStrategyProto & search_strategy(int index) const
std::function< void(Model *)> AllDifferentAC(const std::vector< IntegerVariable > &variables)
Definition: all_different.h:59
void AddTableConstraint(absl::Span< const IntegerVariable > vars, std::vector< std::vector< int64_t >> tuples, Model *model)
Definition: sat/table.cc:250
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:888
void AddReservoirConstraint(std::vector< AffineExpression > times, std::vector< IntegerValue > deltas, std::vector< Literal > presences, int64_t min_level, int64_t max_level, Model *model)
Definition: timetable.cc:28
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1349
void LoadBoolXorConstraint(const ConstraintProto &ct, Model *m)
void LoadIntMaxConstraint(const ConstraintProto &ct, Model *m)
bool AddProblemClause(absl::Span< const Literal > literals)
Definition: sat_solver.cc:204
std::function< void(Model *)> Equality(IntegerVariable v, int64_t value)
Definition: integer.h:1582
std::vector< IntervalVariable > Intervals(const ProtoIndices &indices) const
Collection of objects used to extend the Constraint Solver library.
const IntegerVariable kNoIntegerVariable(-1)
std::function< void(Model *)> LiteralXorIs(const std::vector< Literal > &literals, bool value)
FullEncodingFixedPointComputer(const CpModelProto &model_proto, Model *model)
std::function< void(Model *)> AllDifferentOnBounds(const std::vector< IntegerVariable > &vars)
Definition: all_different.h:46
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1309
void assign(size_type n, const value_type &val)
std::function< void(Model *)> NonOverlappingRectangles(const std::vector< IntervalVariable > &x, const std::vector< IntervalVariable > &y, bool is_strict, bool add_cumulative_relaxation=true)
Definition: diffn.h:144
SatParameters parameters
std::function< void(Model *)> WeightedSumGreaterOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
Definition: integer_expr.h:407
bool RefIsPositive(int ref)
const LiteralIndex kNoLiteralIndex(-1)
void ReserveSpaceForNumVariables(int num_vars)
Definition: integer.cc:629
IntVar * var
Definition: expr_array.cc:1874
::PROTOBUF_NAMESPACE_ID::int32 max_domain_size_when_encoding_eq_neq_constraints() const
void LoadBoolOrConstraint(const ConstraintProto &ct, Model *m)
::PROTOBUF_NAMESPACE_ID::int64 domain(int index) const
Definition: cp_model.pb.h:6777
void LoadElementConstraint(const ConstraintProto &ct, Model *m)
const absl::flat_hash_set< int64_t > & PotentialEncodedValues(int var)
void LoadAllDiffConstraint(const ConstraintProto &ct, Model *m)
bool DetectEquivalencesInElementConstraint(const ConstraintProto &ct, Model *m)
#define VLOG_IS_ON(verboselevel)
Definition: vlog_is_on.h:41
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
Definition: sat_solver.h:906
std::vector< IntegerVariable > Integers(const List &list) const
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
Definition: sat_solver.h:878
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
IntegerVariable Integer(int ref) const
bool IsEmpty() const
Returns true if this is the empty set.
void LoadLinearConstraint(const ConstraintProto &ct, Model *m)
int64_t value
std::function< void(Model *)> Cumulative(const std::vector< IntervalVariable > &vars, const std::vector< AffineExpression > &demands, AffineExpression capacity, SchedulingConstraintHelper *helper=nullptr)
Definition: cumulative.h:45
Literal literal
Definition: optimization.cc:85
bool IsFixed(IntegerVariable i) const
Definition: integer.h:1353
const Constraint * ct
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:889
IndexReferences GetReferencesUsedByConstraint(const ConstraintProto &ct)
void LoadVariables(const CpModelProto &model_proto, bool view_all_booleans_as_integers, Model *m)
void LoadBoolAndConstraint(const ConstraintProto &ct, Model *m)
void LoadBooleanSymmetries(const CpModelProto &model_proto, Model *m)
const IntervalVariable kNoIntervalVariable(-1)