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