OR-Tools  9.6
cp_model_loader.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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 <cmath>
18 #include <cstdint>
19 #include <limits>
20 #include <memory>
21 #include <numeric>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 #include "absl/container/btree_set.h"
27 #include "absl/container/flat_hash_map.h"
28 #include "absl/container/flat_hash_set.h"
29 #include "absl/strings/str_cat.h"
30 #include "absl/types/span.h"
32 #include "ortools/base/logging.h"
33 #include "ortools/base/stl_util.h"
36 #include "ortools/sat/circuit.h"
38 #include "ortools/sat/cp_model.pb.h"
41 #include "ortools/sat/cumulative.h"
42 #include "ortools/sat/diffn.h"
45 #include "ortools/sat/integer.h"
47 #include "ortools/sat/intervals.h"
49 #include "ortools/sat/model.h"
51 #include "ortools/sat/sat_base.h"
52 #include "ortools/sat/sat_parameters.pb.h"
53 #include "ortools/sat/sat_solver.h"
54 #include "ortools/sat/symmetry.h"
55 #include "ortools/sat/timetable.h"
56 #include "ortools/util/logging.h"
59 
60 namespace operations_research {
61 namespace sat {
62 
63 namespace {
64 
65 template <typename Values>
66 std::vector<int64_t> ValuesFromProto(const Values& values) {
67  return std::vector<int64_t>(values.begin(), values.end());
68 }
69 
70 void ComputeLinearBounds(const LinearConstraintProto& proto,
71  CpModelMapping* mapping, IntegerTrail* integer_trail,
72  int64_t* sum_min, int64_t* sum_max) {
73  *sum_min = 0;
74  *sum_max = 0;
75 
76  for (int i = 0; i < proto.vars_size(); ++i) {
77  const int64_t coeff = proto.coeffs(i);
78  const IntegerVariable var = mapping->Integer(proto.vars(i));
79  const int64_t lb = integer_trail->LowerBound(var).value();
80  const int64_t ub = integer_trail->UpperBound(var).value();
81  if (coeff >= 0) {
82  (*sum_min) += coeff * lb;
83  (*sum_max) += coeff * ub;
84  } else {
85  (*sum_min) += coeff * ub;
86  (*sum_max) += coeff * lb;
87  }
88  }
89 }
90 
91 // We check if the constraint is a sum(ax * xi) == value.
92 bool ConstraintIsEq(const LinearConstraintProto& proto) {
93  return proto.domain_size() == 2 && proto.domain(0) == proto.domain(1);
94 }
95 
96 // We check if the constraint is a sum(ax * xi) != value.
97 bool ConstraintIsNEq(const LinearConstraintProto& proto,
98  CpModelMapping* mapping, IntegerTrail* integer_trail,
99  int64_t* single_value) {
100  int64_t sum_min = 0;
101  int64_t sum_max = 0;
102  ComputeLinearBounds(proto, mapping, integer_trail, &sum_min, &sum_max);
103 
104  const Domain complement =
105  Domain(sum_min, sum_max)
106  .IntersectionWith(ReadDomainFromProto(proto).Complement());
107  if (complement.IsEmpty()) return false;
108  const int64_t value = complement.Min();
109 
110  if (complement.Size() == 1) {
111  if (single_value != nullptr) {
112  *single_value = value;
113  }
114  return true;
115  }
116  return false;
117 }
118 
119 } // namespace
120 
121 void LoadVariables(const CpModelProto& model_proto,
122  bool view_all_booleans_as_integers, Model* m) {
123  auto* mapping = m->GetOrCreate<CpModelMapping>();
124  const int num_proto_variables = model_proto.variables_size();
125 
126  // All [0, 1] variables always have a corresponding Boolean, even if it is
127  // fixed to 0 (domain == [0,0]) or fixed to 1 (domain == [1,1]).
128  {
129  auto* sat_solver = m->GetOrCreate<SatSolver>();
130  CHECK_EQ(sat_solver->NumVariables(), 0);
131 
132  BooleanVariable new_var(0);
133  std::vector<BooleanVariable> false_variables;
134  std::vector<BooleanVariable> true_variables;
135 
136  mapping->booleans_.resize(num_proto_variables, kNoBooleanVariable);
137  mapping->reverse_boolean_map_.resize(num_proto_variables, -1);
138  for (int i = 0; i < num_proto_variables; ++i) {
139  const auto& domain = model_proto.variables(i).domain();
140  if (domain.size() != 2) continue;
141  if (domain[0] >= 0 && domain[1] <= 1) {
142  mapping->booleans_[i] = new_var;
143  mapping->reverse_boolean_map_[new_var] = i;
144  if (domain[1] == 0) {
145  false_variables.push_back(new_var);
146  } else if (domain[0] == 1) {
147  true_variables.push_back(new_var);
148  }
149  ++new_var;
150  }
151  }
152 
153  sat_solver->SetNumVariables(new_var.value());
154  for (const BooleanVariable var : true_variables) {
155  m->Add(ClauseConstraint({sat::Literal(var, true)}));
156  }
157  for (const BooleanVariable var : false_variables) {
158  m->Add(ClauseConstraint({sat::Literal(var, false)}));
159  }
160  }
161 
162  // Compute the list of positive variable reference for which we need to
163  // create an IntegerVariable.
164  std::vector<int> var_to_instantiate_as_integer;
165  if (view_all_booleans_as_integers) {
166  var_to_instantiate_as_integer.resize(num_proto_variables);
167  for (int i = 0; i < num_proto_variables; ++i) {
168  var_to_instantiate_as_integer[i] = i;
169  }
170  } else {
171  // Compute the integer variable references used by the model.
172  absl::flat_hash_set<int> used_variables;
173 
174  IndexReferences refs;
175  for (int c = 0; c < model_proto.constraints_size(); ++c) {
176  const ConstraintProto& ct = model_proto.constraints(c);
178  for (const int ref : refs.variables) {
179  used_variables.insert(PositiveRef(ref));
180  }
181  }
182 
183  // Add the objectives variables that needs to be referenceable as integer
184  // even if they are only used as Booleans.
185  if (model_proto.has_objective()) {
186  for (const int obj_var : model_proto.objective().vars()) {
187  used_variables.insert(PositiveRef(obj_var));
188  }
189  }
190 
191  // Make sure any unused variable, that is not already a Boolean is
192  // considered "used".
193  for (int i = 0; i < num_proto_variables; ++i) {
194  if (mapping->booleans_[i] == kNoBooleanVariable) {
195  used_variables.insert(i);
196  }
197  }
198 
199  // We want the variable in the problem order.
200  var_to_instantiate_as_integer.assign(used_variables.begin(),
201  used_variables.end());
202  gtl::STLSortAndRemoveDuplicates(&var_to_instantiate_as_integer);
203  }
204  mapping->integers_.resize(num_proto_variables, kNoIntegerVariable);
205 
206  // It is important for memory usage to reserve tight vector has we have many
207  // indexed by IntegerVariable. Unfortunately, we create intermediate
208  // IntegerVariable while loading large linear constraint, or when we have
209  // disjoint LP component. So this is a best effort at a tight upper bound.
210  int reservation_size = var_to_instantiate_as_integer.size();
211  for (const ConstraintProto& ct : model_proto.constraints()) {
212  if (ct.constraint_case() != ConstraintProto::kLinear) continue;
213  const int ct_size = ct.linear().vars().size();
214  if (ct_size > 100) {
215  reservation_size += static_cast<int>(std::round(std::sqrt(ct_size)));
216  }
217  }
218  if (model_proto.has_objective()) {
219  reservation_size += 1; // Objective var.
220  const int ct_size = model_proto.objective().vars().size() + 1;
221  if (ct_size > 100) {
222  reservation_size += static_cast<int>(std::round(std::sqrt(ct_size)));
223  }
224  }
225 
226  auto* integer_trail = m->GetOrCreate<IntegerTrail>();
227  integer_trail->ReserveSpaceForNumVariables(reservation_size);
228  m->GetOrCreate<GenericLiteralWatcher>()->ReserveSpaceForNumVariables(
229  reservation_size);
230  mapping->reverse_integer_map_.resize(2 * var_to_instantiate_as_integer.size(),
231  -1);
232  for (const int i : var_to_instantiate_as_integer) {
233  const auto& var_proto = model_proto.variables(i);
234  mapping->integers_[i] =
235  integer_trail->AddIntegerVariable(ReadDomainFromProto(var_proto));
236  DCHECK_LT(mapping->integers_[i], mapping->reverse_integer_map_.size());
237  mapping->reverse_integer_map_[mapping->integers_[i]] = i;
238  }
239 
240  auto* encoder = m->GetOrCreate<IntegerEncoder>();
241  auto* intervals_repository = m->GetOrCreate<IntervalsRepository>();
242 
243  // Link any variable that has both views.
244  for (int i = 0; i < num_proto_variables; ++i) {
245  if (mapping->integers_[i] == kNoIntegerVariable) continue;
246  if (mapping->booleans_[i] == kNoBooleanVariable) continue;
247 
248  // Associate with corresponding integer variable.
249  encoder->AssociateToIntegerEqualValue(
250  sat::Literal(mapping->booleans_[i], true), mapping->integers_[i],
251  IntegerValue(1));
252  }
253 
254  // Create the interval variables.
255  mapping->intervals_.resize(model_proto.constraints_size(),
257  for (int c = 0; c < model_proto.constraints_size(); ++c) {
258  const ConstraintProto& ct = model_proto.constraints(c);
259  if (ct.constraint_case() != ConstraintProto::ConstraintCase::kInterval) {
260  continue;
261  }
262  if (HasEnforcementLiteral(ct)) {
263  const sat::Literal enforcement_literal =
264  mapping->Literal(ct.enforcement_literal(0));
265  // TODO(user): Fix the constant variable situation. An optional interval
266  // with constant start/end or size cannot share the same constant
267  // variable if it is used in non-optional situation.
268  mapping->intervals_[c] = intervals_repository->CreateInterval(
269  mapping->Affine(ct.interval().start()),
270  mapping->Affine(ct.interval().end()),
271  mapping->Affine(ct.interval().size()), enforcement_literal.Index(),
272  /*add_linear_relation=*/false);
273  } else {
274  mapping->intervals_[c] = intervals_repository->CreateInterval(
275  mapping->Affine(ct.interval().start()),
276  mapping->Affine(ct.interval().end()),
277  mapping->Affine(ct.interval().size()), kNoLiteralIndex,
278  /*add_linear_relation=*/false);
279  }
280  mapping->already_loaded_ct_.insert(&ct);
281  }
282 }
283 
284 void LoadBooleanSymmetries(const CpModelProto& model_proto, Model* m) {
285  auto* mapping = m->GetOrCreate<CpModelMapping>();
286  const SymmetryProto& symmetry = model_proto.symmetry();
287  if (symmetry.permutations().empty()) return;
288 
289  // We currently can only use symmetry that touch a subset of variables.
290  const int num_vars = model_proto.variables().size();
291  std::vector<bool> can_be_used_in_symmetry(num_vars, true);
292 
293  // First, we currently only support loading symmetry between Booleans.
294  for (int v = 0; v < num_vars; ++v) {
295  if (!mapping->IsBoolean(v)) can_be_used_in_symmetry[v] = false;
296  }
297 
298  // Tricky: Moreover, some constraint will causes extra Boolean to be created
299  // and linked with the Boolean in the constraints. We can't use any of the
300  // symmetry that touch these since we potentially miss the component that will
301  // map these extra Booleans between each other.
302  //
303  // TODO(user): We could add these extra Boolean during expansion/presolve so
304  // that we have the symmetry involing them. Or maybe comes up with a different
305  // solution.
306  const int num_constraints = model_proto.constraints().size();
307  for (int c = 0; c < num_constraints; ++c) {
308  const ConstraintProto& ct = model_proto.constraints(c);
309  if (ct.constraint_case() != ConstraintProto::kLinear) continue;
310  if (ct.linear().domain().size() <= 2) continue;
311 
312  // A linear with a complex domain might need extra Booleans to be loaded.
313  // Note that it should be fine for the Boolean(s) in enforcement_literal
314  // though.
315  for (const int ref : ct.linear().vars()) {
316  can_be_used_in_symmetry[PositiveRef(ref)] = false;
317  }
318  }
319 
320  auto* sat_solver = m->GetOrCreate<SatSolver>();
321  auto* symmetry_handler = m->GetOrCreate<SymmetryPropagator>();
322  sat_solver->AddPropagator(symmetry_handler);
323  const int num_literals = 2 * sat_solver->NumVariables();
324 
325  for (const SparsePermutationProto& perm : symmetry.permutations()) {
326  bool can_be_used = true;
327  for (const int var : perm.support()) {
328  if (!can_be_used_in_symmetry[var]) {
329  can_be_used = false;
330  break;
331  }
332  }
333  if (!can_be_used) continue;
334 
335  // Convert the variable symmetry to a "literal" one.
336  auto literal_permutation =
337  std::make_unique<SparsePermutation>(num_literals);
338  int support_index = 0;
339  const int num_cycle = perm.cycle_sizes().size();
340  for (int i = 0; i < num_cycle; ++i) {
341  const int size = perm.cycle_sizes(i);
342  const int saved_support_index = support_index;
343  for (int j = 0; j < size; ++j) {
344  const int var = perm.support(support_index++);
345  literal_permutation->AddToCurrentCycle(
346  mapping->Literal(var).Index().value());
347  }
348  literal_permutation->CloseCurrentCycle();
349 
350  // Note that we also need to add the corresponding cycle for the negated
351  // literals.
352  support_index = saved_support_index;
353  for (int j = 0; j < size; ++j) {
354  const int var = perm.support(support_index++);
355  literal_permutation->AddToCurrentCycle(
356  mapping->Literal(var).NegatedIndex().value());
357  }
358  literal_permutation->CloseCurrentCycle();
359  }
360  symmetry_handler->AddSymmetry(std::move(literal_permutation));
361  }
362 
363  SOLVER_LOG(m->GetOrCreate<SolverLogger>(), "Added ",
364  symmetry_handler->num_permutations(),
365  " symmetry to the SAT solver.");
366 }
367 
368 // The logic assumes that the linear constraints have been presolved, so that
369 // equality with a domain bound have been converted to <= or >= and so that we
370 // never have any trivial inequalities.
371 //
372 // TODO(user): Regroup/presolve two encoding like b => x > 2 and the same
373 // Boolean b => x > 5. These shouldn't happen if we merge linear constraints.
374 void ExtractEncoding(const CpModelProto& model_proto, Model* m) {
375  auto* mapping = m->GetOrCreate<CpModelMapping>();
376  auto* encoder = m->GetOrCreate<IntegerEncoder>();
377  auto* integer_trail = m->GetOrCreate<IntegerTrail>();
378  auto* sat_solver = m->GetOrCreate<SatSolver>();
379 
380  // TODO(user): Debug what makes it unsat at this point.
381  if (sat_solver->ModelIsUnsat()) return;
382 
383  // Detection of literal equivalent to (i_var == value). We collect all the
384  // half-reified constraint lit => equality or lit => inequality for a given
385  // variable, and we will later sort them to detect equivalence.
386  struct EqualityDetectionHelper {
387  const ConstraintProto* ct;
389  int64_t value;
390  bool is_equality; // false if != instead.
391 
392  bool operator<(const EqualityDetectionHelper& o) const {
393  if (literal.Variable() == o.literal.Variable()) {
394  if (value == o.value) return is_equality && !o.is_equality;
395  return value < o.value;
396  }
397  return literal.Variable() < o.literal.Variable();
398  }
399  };
400  std::vector<std::vector<EqualityDetectionHelper>> var_to_equalities(
401  model_proto.variables_size());
402 
403  // TODO(user): We will re-add the same implied bounds during probing, so
404  // it might not be necessary to do that here. Also, it might be too early
405  // if some of the literal view used in the LP are created later, but that
406  // should be fixable via calls to implied_bounds->NotifyNewIntegerView().
407  auto* implied_bounds = m->GetOrCreate<ImpliedBounds>();
408  auto* detector = m->GetOrCreate<ProductDetector>();
409 
410  // Detection of literal equivalent to (i_var >= bound). We also collect
411  // all the half-refied part and we will sort the vector for detection of the
412  // equivalence.
413  struct InequalityDetectionHelper {
414  const ConstraintProto* ct;
416  IntegerLiteral i_lit;
417 
418  bool operator<(const InequalityDetectionHelper& o) const {
419  if (literal.Variable() == o.literal.Variable()) {
420  return i_lit.var < o.i_lit.var;
421  }
422  return literal.Variable() < o.literal.Variable();
423  }
424  };
425  std::vector<InequalityDetectionHelper> inequalities;
426 
427  // Loop over all constraints and fill var_to_equalities and inequalities.
428  for (const ConstraintProto& ct : model_proto.constraints()) {
429  if (ct.constraint_case() != ConstraintProto::ConstraintCase::kLinear) {
430  continue;
431  }
432  if (ct.enforcement_literal().size() != 1) continue;
433  if (ct.linear().vars_size() != 1) continue;
434 
435  // ct is a linear constraint with one term and one enforcement literal.
436  const sat::Literal enforcement_literal =
437  mapping->Literal(ct.enforcement_literal(0));
438  if (sat_solver->Assignment().LiteralIsFalse(enforcement_literal)) continue;
439 
440  const int ref = ct.linear().vars(0);
441  const int var = PositiveRef(ref);
442 
443  const Domain domain = ReadDomainFromProto(model_proto.variables(var));
444  const Domain domain_if_enforced =
445  ReadDomainFromProto(ct.linear())
446  .InverseMultiplicationBy(ct.linear().coeffs(0) *
447  (RefIsPositive(ref) ? 1 : -1));
448 
449  if (domain_if_enforced.IsEmpty()) {
450  if (!sat_solver->AddUnitClause(enforcement_literal.Negated())) return;
451  continue;
452  }
453 
454  // Detect enforcement_literal => (var >= value or var <= value).
455  if (domain_if_enforced.NumIntervals() == 1) {
456  if (domain_if_enforced.Max() >= domain.Max() &&
457  domain_if_enforced.Min() > domain.Min()) {
458  inequalities.push_back({&ct, enforcement_literal,
460  mapping->Integer(var),
461  IntegerValue(domain_if_enforced.Min()))});
462  } else if (domain_if_enforced.Min() <= domain.Min() &&
463  domain_if_enforced.Max() < domain.Max()) {
464  inequalities.push_back({&ct, enforcement_literal,
466  mapping->Integer(var),
467  IntegerValue(domain_if_enforced.Max()))});
468  }
469  }
470 
471  // Detect implied bounds. The test is less strict than the above
472  // test.
473  if (domain_if_enforced.Min() > domain.Min()) {
474  implied_bounds->Add(
475  enforcement_literal,
477  mapping->Integer(var), IntegerValue(domain_if_enforced.Min())));
478  }
479  if (domain_if_enforced.Max() < domain.Max()) {
480  implied_bounds->Add(
481  enforcement_literal,
482  IntegerLiteral::LowerOrEqual(mapping->Integer(var),
483  IntegerValue(domain_if_enforced.Max())));
484  }
485 
486  // Detect enforcement_literal => (var == value or var != value).
487  //
488  // Note that for domain with 2 values like [0, 1], we will detect both ==
489  // 0 and != 1. Similarly, for a domain in [min, max], we should both
490  // detect (== min) and (<= min), and both detect (== max) and (>= max).
491  {
492  const Domain inter = domain.IntersectionWith(domain_if_enforced);
493  if (!inter.IsEmpty() && inter.Min() == inter.Max()) {
494  if (inter.Min() == 0) {
495  detector->ProcessConditionalZero(enforcement_literal,
496  mapping->Integer(var));
497  }
498  var_to_equalities[var].push_back(
499  {&ct, enforcement_literal, inter.Min(), true});
500  }
501  }
502  {
503  const Domain inter =
504  domain.IntersectionWith(domain_if_enforced.Complement());
505  if (!inter.IsEmpty() && inter.Min() == inter.Max()) {
506  var_to_equalities[var].push_back(
507  {&ct, enforcement_literal, inter.Min(), false});
508  }
509  }
510  }
511 
512  // Detect Literal <=> X >= value
513  int num_inequalities = 0;
514  std::sort(inequalities.begin(), inequalities.end());
515  for (int i = 0; i + 1 < inequalities.size(); i++) {
516  if (inequalities[i].literal != inequalities[i + 1].literal.Negated()) {
517  continue;
518  }
519 
520  // TODO(user): In these cases, we could fix the enforcement literal right
521  // away or ignore the constraint. Note that it will be done later anyway
522  // though.
523  if (integer_trail->IntegerLiteralIsTrue(inequalities[i].i_lit) ||
524  integer_trail->IntegerLiteralIsFalse(inequalities[i].i_lit)) {
525  continue;
526  }
527  if (integer_trail->IntegerLiteralIsTrue(inequalities[i + 1].i_lit) ||
528  integer_trail->IntegerLiteralIsFalse(inequalities[i + 1].i_lit)) {
529  continue;
530  }
531 
532  const auto pair_a = encoder->Canonicalize(inequalities[i].i_lit);
533  const auto pair_b = encoder->Canonicalize(inequalities[i + 1].i_lit);
534  if (pair_a.first == pair_b.second) {
535  ++num_inequalities;
536  encoder->AssociateToIntegerLiteral(inequalities[i].literal,
537  inequalities[i].i_lit);
538  mapping->already_loaded_ct_.insert(inequalities[i].ct);
539  mapping->already_loaded_ct_.insert(inequalities[i + 1].ct);
540  }
541  }
542 
543  // Encode the half-inequalities.
544  int num_half_inequalities = 0;
545  for (const auto inequality : inequalities) {
546  if (mapping->ConstraintIsAlreadyLoaded(inequality.ct)) continue;
547  m->Add(
548  Implication(inequality.literal,
549  encoder->GetOrCreateAssociatedLiteral(inequality.i_lit)));
550  if (sat_solver->ModelIsUnsat()) return;
551 
552  ++num_half_inequalities;
553  mapping->already_loaded_ct_.insert(inequality.ct);
554  mapping->is_half_encoding_ct_.insert(inequality.ct);
555  }
556 
557  if (!inequalities.empty()) {
558  VLOG(1) << num_inequalities << " literals associated to VAR >= value, and "
559  << num_half_inequalities << " half-associations.";
560  }
561 
562  // Detect Literal <=> X == value and associate them in the IntegerEncoder.
563  //
564  // TODO(user): Fully encode variable that are almost fully encoded?
565  int num_constraints = 0;
566  int num_equalities = 0;
567  int num_half_equalities = 0;
568  int num_fully_encoded = 0;
569  int num_partially_encoded = 0;
570  for (int i = 0; i < var_to_equalities.size(); ++i) {
571  std::vector<EqualityDetectionHelper>& encoding = var_to_equalities[i];
572  std::sort(encoding.begin(), encoding.end());
573  if (encoding.empty()) continue;
574  num_constraints += encoding.size();
575 
576  absl::flat_hash_set<int64_t> values;
577  for (int j = 0; j + 1 < encoding.size(); j++) {
578  if ((encoding[j].value != encoding[j + 1].value) ||
579  (encoding[j].literal != encoding[j + 1].literal.Negated()) ||
580  (encoding[j].is_equality != true) ||
581  (encoding[j + 1].is_equality != false)) {
582  continue;
583  }
584 
585  ++num_equalities;
586  encoder->AssociateToIntegerEqualValue(encoding[j].literal,
587  mapping->integers_[i],
588  IntegerValue(encoding[j].value));
589  mapping->already_loaded_ct_.insert(encoding[j].ct);
590  mapping->already_loaded_ct_.insert(encoding[j + 1].ct);
591  values.insert(encoding[j].value);
592  }
593 
594  // TODO(user): Try to remove it. Normally we caught UNSAT above, but
595  // tests are very flaky (it only happens in parallel). Keeping it there for
596  // the time being.
597  if (sat_solver->ModelIsUnsat()) return;
598 
599  // Encode the half-equalities.
600  //
601  // TODO(user): delay this after PropagateEncodingFromEquivalenceRelations()?
602  // Otherwise we might create new Boolean variables for no reason. Note
603  // however, that in the presolve, we should only use the "representative" in
604  // linear constraints, so we should be fine.
605  for (const auto equality : encoding) {
606  if (mapping->ConstraintIsAlreadyLoaded(equality.ct)) continue;
607  const class Literal eq = encoder->GetOrCreateLiteralAssociatedToEquality(
608  mapping->integers_[i], IntegerValue(equality.value));
609  if (equality.is_equality) {
610  m->Add(Implication(equality.literal, eq));
611  } else {
612  m->Add(Implication(equality.literal, eq.Negated()));
613  }
614 
615  ++num_half_equalities;
616  mapping->already_loaded_ct_.insert(equality.ct);
617  mapping->is_half_encoding_ct_.insert(equality.ct);
618  }
619 
620  // Update stats.
621  if (VLOG_IS_ON(1)) {
622  if (encoder->VariableIsFullyEncoded(mapping->integers_[i])) {
623  ++num_fully_encoded;
624  } else {
625  ++num_partially_encoded;
626  }
627  }
628  }
629 
630  if (num_constraints > 0) {
631  VLOG(1) << num_equalities << " literals associated to VAR == value, and "
632  << num_half_equalities << " half-associations.";
633  }
634  if (num_fully_encoded > 0) {
635  VLOG(1) << "num_fully_encoded_variables: " << num_fully_encoded;
636  }
637  if (num_partially_encoded > 0) {
638  VLOG(1) << "num_partially_encoded_variables: " << num_partially_encoded;
639  }
640 }
641 
642 void ExtractElementEncoding(const CpModelProto& model_proto, Model* m) {
643  int num_element_encoded = 0;
644  auto* mapping = m->GetOrCreate<CpModelMapping>();
645  auto* implied_bounds = m->GetOrCreate<ImpliedBounds>();
646 
647  // Scan all exactly_one constraints and look for literal => var == value to
648  // detect element encodings.
649  for (int c = 0; c < model_proto.constraints_size(); ++c) {
650  const ConstraintProto& ct = model_proto.constraints(c);
651 
652  if (ct.constraint_case() != ConstraintProto::kExactlyOne) continue;
653 
654  // Project the implied values onto each integer variable.
655  absl::flat_hash_map<IntegerVariable, std::vector<ValueLiteralPair>>
656  var_to_value_literal_list;
657  for (const int l : ct.exactly_one().literals()) {
658  const Literal literal = mapping->Literal(l);
659  for (const auto& var_value : implied_bounds->GetImpliedValues(literal)) {
660  var_to_value_literal_list[var_value.first].push_back(
661  {var_value.second, literal});
662  }
663  }
664 
665  // VLOG info.
666  std::vector<IntegerVariable> encoded_variables;
667  std::string encoded_variables_str;
668 
669  // Search for variable fully covered by the literals of the exactly_one.
670  for (const auto& [var, literal_value_list] : var_to_value_literal_list) {
671  if (literal_value_list.size() < ct.exactly_one().literals_size()) {
672  VLOG(2) << "X" << var.value() << " has " << literal_value_list.size()
673  << " implied values, and a domain of size "
674  << m->GetOrCreate<IntegerTrail>()
675  ->InitialVariableDomain(var)
676  .Size();
677  continue;
678  }
679 
680  // We use the order of literals of the exactly_one.
681  implied_bounds->AddElementEncoding(var, literal_value_list, c);
682  if (VLOG_IS_ON(1)) {
683  encoded_variables.push_back(var);
684  absl::StrAppend(&encoded_variables_str, " X", var.value());
685  num_element_encoded++;
686  }
687  }
688  if (encoded_variables.size() > 1 && VLOG_IS_ON(1)) {
689  VLOG(1) << "exactly_one(" << c << ") encodes " << encoded_variables.size()
690  << " variables at the same time: " << encoded_variables_str;
691  }
692  }
693 
694  if (num_element_encoded > 0) {
695  VLOG(1) << "num_element_encoded: " << num_element_encoded;
696  }
697 }
698 
700  Model* m) {
701  auto* mapping = m->GetOrCreate<CpModelMapping>();
702  auto* encoder = m->GetOrCreate<IntegerEncoder>();
703  auto* sat_solver = m->GetOrCreate<SatSolver>();
704 
705  // Loop over all constraints and find affine ones.
706  int64_t num_associations = 0;
707  int64_t num_set_to_false = 0;
708  for (const ConstraintProto& ct : model_proto.constraints()) {
709  if (!ct.enforcement_literal().empty()) continue;
710  if (ct.constraint_case() != ConstraintProto::kLinear) continue;
711  if (ct.linear().vars_size() != 2) continue;
712  if (!ConstraintIsEq(ct.linear())) continue;
713 
714  const IntegerValue rhs(ct.linear().domain(0));
715 
716  // Make sure the coefficient are positive.
717  IntegerVariable var1 = mapping->Integer(ct.linear().vars(0));
718  IntegerVariable var2 = mapping->Integer(ct.linear().vars(1));
719  IntegerValue coeff1(ct.linear().coeffs(0));
720  IntegerValue coeff2(ct.linear().coeffs(1));
721  if (coeff1 < 0) {
722  var1 = NegationOf(var1);
723  coeff1 = -coeff1;
724  }
725  if (coeff2 < 0) {
726  var2 = NegationOf(var2);
727  coeff2 = -coeff2;
728  }
729 
730  // TODO(user): This is not supposed to happen, but apparently it did on
731  // once on routing_GCM_0001_sat.fzn. Investigate and fix.
732  if (coeff1 == 0 || coeff2 == 0) continue;
733 
734  // We first map the >= literals.
735  // It is important to do that first, since otherwise mapping a == literal
736  // might creates the underlying >= and <= literals.
737  for (int i = 0; i < 2; ++i) {
738  for (const auto [value1, literal1] :
739  encoder->PartialGreaterThanEncoding(var1)) {
740  const IntegerValue bound2 = FloorRatio(rhs - value1 * coeff1, coeff2);
741  ++num_associations;
742  encoder->AssociateToIntegerLiteral(
743  literal1, IntegerLiteral::LowerOrEqual(var2, bound2));
744  }
745  std::swap(var1, var2);
746  std::swap(coeff1, coeff2);
747  }
748 
749  // Same for the == literals.
750  //
751  // TODO(user): This is similar to LoadEquivalenceAC() for unreified
752  // constraints, but when the later is called, more encoding might have taken
753  // place.
754  for (int i = 0; i < 2; ++i) {
755  for (const auto value_literal : encoder->PartialDomainEncoding(var1)) {
756  const IntegerValue value1 = value_literal.value;
757  const IntegerValue intermediate = rhs - value1 * coeff1;
758  if (intermediate % coeff2 != 0) {
759  // Using this function deals properly with UNSAT.
760  ++num_set_to_false;
761  sat_solver->AddUnitClause(value_literal.literal.Negated());
762  continue;
763  }
764  ++num_associations;
765  encoder->AssociateToIntegerEqualValue(value_literal.literal, var2,
766  intermediate / coeff2);
767  }
768  std::swap(var1, var2);
769  std::swap(coeff1, coeff2);
770  }
771  }
772 
773  if (num_associations > 0) {
774  VLOG(1) << "Num associations from equivalences = " << num_associations;
775  }
776  if (num_set_to_false > 0) {
777  VLOG(1) << "Num literals set to false from equivalences = "
778  << num_set_to_false;
779  }
780 }
781 
782 void DetectOptionalVariables(const CpModelProto& model_proto, Model* m) {
783  auto* mapping = m->GetOrCreate<CpModelMapping>();
784  const SatParameters& parameters = *(m->GetOrCreate<SatParameters>());
785  if (!parameters.use_optional_variables()) return;
786  if (parameters.enumerate_all_solutions()) return;
787 
788  // The variables from the objective cannot be marked as optional!
789  const int num_proto_variables = model_proto.variables_size();
790  std::vector<bool> already_seen(num_proto_variables, false);
791  if (model_proto.has_objective()) {
792  for (const int ref : model_proto.objective().vars()) {
793  already_seen[PositiveRef(ref)] = true;
794  }
795  }
796 
797  // Compute for each variables the intersection of the enforcement literals
798  // of the constraints in which they appear.
799  //
800  // TODO(user): This deals with the simplest cases, but we could try to
801  // detect literals that implies all the constraints in which a variable
802  // appear to false. This can be done with a LCA computation in the tree of
803  // Boolean implication (once the presolve remove cycles). Not sure if we can
804  // properly exploit that afterwards though. Do some research!
805  std::vector<std::vector<int>> enforcement_intersection(num_proto_variables);
806  absl::btree_set<int> literals_set;
807  for (int c = 0; c < model_proto.constraints_size(); ++c) {
808  const ConstraintProto& ct = model_proto.constraints(c);
809  if (ct.enforcement_literal().empty()) {
810  for (const int var : UsedVariables(ct)) {
811  already_seen[var] = true;
812  enforcement_intersection[var].clear();
813  }
814  } else {
815  literals_set.clear();
816  literals_set.insert(ct.enforcement_literal().begin(),
817  ct.enforcement_literal().end());
818  for (const int var : UsedVariables(ct)) {
819  if (!already_seen[var]) {
820  enforcement_intersection[var].assign(ct.enforcement_literal().begin(),
821  ct.enforcement_literal().end());
822  } else {
823  // Take the intersection.
824  std::vector<int>& vector_ref = enforcement_intersection[var];
825  int new_size = 0;
826  for (const int literal : vector_ref) {
827  if (literals_set.contains(literal)) {
828  vector_ref[new_size++] = literal;
829  }
830  }
831  vector_ref.resize(new_size);
832  }
833  already_seen[var] = true;
834  }
835  }
836  }
837 
838  // Auto-detect optional variables.
839  int num_optionals = 0;
840  auto* integer_trail = m->GetOrCreate<IntegerTrail>();
841  for (int var = 0; var < num_proto_variables; ++var) {
842  const IntegerVariableProto& var_proto = model_proto.variables(var);
843  const int64_t min = var_proto.domain(0);
844  const int64_t max = var_proto.domain(var_proto.domain().size() - 1);
845  if (min == max) continue;
846  if (min == 0 && max == 1) continue;
847  if (enforcement_intersection[var].empty()) continue;
848 
849  ++num_optionals;
850  integer_trail->MarkIntegerVariableAsOptional(
851  mapping->Integer(var),
852  mapping->Literal(enforcement_intersection[var].front()));
853  }
854 
855  if (num_optionals > 0) {
856  SOLVER_LOG(m->GetOrCreate<SolverLogger>(), "Auto-detected ", num_optionals,
857  " optional variables.");
858  }
859 }
860 
862  Model* m) {
863  if (model_proto.search_strategy().empty()) return;
864 
865  auto* mapping = m->GetOrCreate<CpModelMapping>();
866  auto* integer_trail = m->GetOrCreate<IntegerTrail>();
867  for (const DecisionStrategyProto& strategy : model_proto.search_strategy()) {
868  if (strategy.domain_reduction_strategy() ==
869  DecisionStrategyProto::SELECT_MEDIAN_VALUE) {
870  for (const int ref : strategy.variables()) {
871  if (!mapping->IsInteger(ref)) continue;
872  const IntegerVariable variable = mapping->Integer(PositiveRef(ref));
873  if (!integer_trail->IsFixed(variable)) {
874  m->Add(FullyEncodeVariable(variable));
875  }
876  }
877  }
878  }
879 }
880 
881 // ============================================================================
882 // Constraint loading functions.
883 // ============================================================================
884 
885 void LoadBoolOrConstraint(const ConstraintProto& ct, Model* m) {
886  auto* mapping = m->GetOrCreate<CpModelMapping>();
887  std::vector<Literal> literals = mapping->Literals(ct.bool_or().literals());
888  for (const int ref : ct.enforcement_literal()) {
889  literals.push_back(mapping->Literal(ref).Negated());
890  }
891  m->Add(ClauseConstraint(literals));
892  if (literals.size() == 3) {
893  m->GetOrCreate<ProductDetector>()->ProcessTernaryClause(literals);
894  }
895 }
896 
897 void LoadBoolAndConstraint(const ConstraintProto& ct, Model* m) {
898  auto* mapping = m->GetOrCreate<CpModelMapping>();
899  std::vector<Literal> literals;
900  for (const int ref : ct.enforcement_literal()) {
901  literals.push_back(mapping->Literal(ref).Negated());
902  }
903  auto* sat_solver = m->GetOrCreate<SatSolver>();
904  for (const Literal literal : mapping->Literals(ct.bool_and().literals())) {
905  literals.push_back(literal);
906  sat_solver->AddProblemClause(literals, /*is_safe=*/false);
907  literals.pop_back();
908  }
909 }
910 
911 void LoadAtMostOneConstraint(const ConstraintProto& ct, Model* m) {
912  auto* mapping = m->GetOrCreate<CpModelMapping>();
913  CHECK(!HasEnforcementLiteral(ct)) << "Not supported.";
914  m->Add(AtMostOneConstraint(mapping->Literals(ct.at_most_one().literals())));
915 }
916 
917 void LoadExactlyOneConstraint(const ConstraintProto& ct, Model* m) {
918  auto* mapping = m->GetOrCreate<CpModelMapping>();
919  CHECK(!HasEnforcementLiteral(ct)) << "Not supported.";
920  const auto& literals = mapping->Literals(ct.exactly_one().literals());
921  m->Add(ExactlyOneConstraint(literals));
922  if (literals.size() == 3) {
923  m->GetOrCreate<ProductDetector>()->ProcessTernaryExactlyOne(literals);
924  }
925 }
926 
927 void LoadBoolXorConstraint(const ConstraintProto& ct, Model* m) {
928  auto* mapping = m->GetOrCreate<CpModelMapping>();
929  CHECK(!HasEnforcementLiteral(ct)) << "Not supported.";
930  m->Add(LiteralXorIs(mapping->Literals(ct.bool_xor().literals()), true));
931 }
932 
933 namespace {
934 
935 // Boolean encoding of:
936 // enforcement_literal => coeff1 * var1 + coeff2 * var2 == rhs;
937 void LoadEquivalenceAC(const std::vector<Literal> enforcement_literal,
938  IntegerValue coeff1, IntegerVariable var1,
939  IntegerValue coeff2, IntegerVariable var2,
940  const IntegerValue rhs, Model* m) {
941  auto* encoder = m->GetOrCreate<IntegerEncoder>();
942  CHECK(encoder->VariableIsFullyEncoded(var1));
943  CHECK(encoder->VariableIsFullyEncoded(var2));
944  absl::flat_hash_map<IntegerValue, Literal> term1_value_to_literal;
945  for (const auto value_literal : encoder->FullDomainEncoding(var1)) {
946  term1_value_to_literal[coeff1 * value_literal.value] =
947  value_literal.literal;
948  }
949  for (const auto value_literal : encoder->FullDomainEncoding(var2)) {
950  const IntegerValue target = rhs - value_literal.value * coeff2;
951  if (!term1_value_to_literal.contains(target)) {
952  m->Add(EnforcedClause(enforcement_literal,
953  {value_literal.literal.Negated()}));
954  } else {
955  const Literal target_literal = term1_value_to_literal[target];
956  m->Add(EnforcedClause(enforcement_literal,
957  {value_literal.literal.Negated(), target_literal}));
958  m->Add(EnforcedClause(enforcement_literal,
959  {value_literal.literal, target_literal.Negated()}));
960 
961  // This "target" can never be reached again, so it is safe to remove it.
962  // We do that so we know the term1 values that are never reached.
963  term1_value_to_literal.erase(target);
964  }
965  }
966 
967  // Exclude the values that can never be "matched" by coeff2 * var2.
968  // We need the std::sort() to be deterministic!
969  std::vector<Literal> implied_false;
970  for (const auto entry : term1_value_to_literal) {
971  implied_false.push_back(entry.second);
972  }
973  std::sort(implied_false.begin(), implied_false.end());
974  for (const Literal l : implied_false) {
975  m->Add(EnforcedClause(enforcement_literal, {l.Negated()}));
976  }
977 }
978 
979 // Boolean encoding of:
980 // enforcement_literal => coeff1 * var1 + coeff2 * var2 != rhs;
981 void LoadEquivalenceNeqAC(const std::vector<Literal> enforcement_literal,
982  IntegerValue coeff1, IntegerVariable var1,
983  IntegerValue coeff2, IntegerVariable var2,
984  const IntegerValue rhs, Model* m) {
985  auto* encoder = m->GetOrCreate<IntegerEncoder>();
986  CHECK(encoder->VariableIsFullyEncoded(var1));
987  CHECK(encoder->VariableIsFullyEncoded(var2));
988  absl::flat_hash_map<IntegerValue, Literal> term1_value_to_literal;
989  for (const auto value_literal : encoder->FullDomainEncoding(var1)) {
990  term1_value_to_literal[coeff1 * value_literal.value] =
991  value_literal.literal;
992  }
993  for (const auto value_literal : encoder->FullDomainEncoding(var2)) {
994  const IntegerValue target_value = rhs - value_literal.value * coeff2;
995  const auto& it = term1_value_to_literal.find(target_value);
996  if (it != term1_value_to_literal.end()) {
997  const Literal target_literal = it->second;
998  m->Add(EnforcedClause(
999  enforcement_literal,
1000  {value_literal.literal.Negated(), target_literal.Negated()}));
1001  }
1002  }
1003 }
1004 
1005 bool IsPartOfProductEncoding(const ConstraintProto& ct) {
1006  if (ct.enforcement_literal().size() != 1) return false;
1007  if (ct.linear().vars().size() > 2) return false;
1008  if (ct.linear().domain().size() != 2) return false;
1009  if (ct.linear().domain(0) != 0) return false;
1010  if (ct.linear().domain(1) != 0) return false;
1011  for (const int64_t coeff : ct.linear().coeffs()) {
1012  if (std::abs(coeff) != 1) return false;
1013  }
1014  return true;
1015 }
1016 
1017 } // namespace
1018 
1019 // TODO(user): We could use a smarter way to determine buckets, like putting
1020 // everyone with the same coeff together if possible and the split is ok.
1021 void SplitAndLoadIntermediateConstraints(bool lb_required, bool ub_required,
1022  std::vector<IntegerVariable>* vars,
1023  std::vector<int64_t>* coeffs,
1024  Model* m) {
1025  // If we enumerate all solutions, then we want intermediate variables to be
1026  // tight independently of what side is required.
1027  if (m->GetOrCreate<SatParameters>()->enumerate_all_solutions()) {
1028  lb_required = true;
1029  ub_required = true;
1030  }
1031 
1032  std::vector<IntegerVariable> bucket_sum_vars;
1033  std::vector<int64_t> bucket_sum_coeffs;
1034  std::vector<IntegerVariable> local_vars;
1035  std::vector<int64_t> local_coeffs;
1036 
1037  int64_t i = 0;
1038  const int64_t num_vars = vars->size();
1039  const int64_t num_buckets = static_cast<int>(std::round(std::sqrt(num_vars)));
1040  auto* integer_trail = m->GetOrCreate<IntegerTrail>();
1041  for (int64_t b = 0; b < num_buckets; ++b) {
1042  local_vars.clear();
1043  local_coeffs.clear();
1044  int64_t bucket_lb = 0;
1045  int64_t bucket_ub = 0;
1046  int64_t gcd = 0;
1047  const int64_t limit = num_vars * (b + 1);
1048  for (; i * num_buckets < limit; ++i) {
1049  const IntegerVariable var = (*vars)[i];
1050  const int64_t coeff = (*coeffs)[i];
1051  gcd = std::gcd(gcd, std::abs(coeff));
1052  local_vars.push_back(var);
1053  local_coeffs.push_back(coeff);
1054  const int64_t term1 = coeff * integer_trail->LowerBound(var).value();
1055  const int64_t term2 = coeff * integer_trail->UpperBound(var).value();
1056  bucket_lb += std::min(term1, term2);
1057  bucket_ub += std::max(term1, term2);
1058  }
1059  if (gcd == 0) continue;
1060  if (gcd > 1) {
1061  // Everything should be exactly divisible!
1062  for (int64_t& ref : local_coeffs) ref /= gcd;
1063  bucket_lb /= gcd;
1064  bucket_ub /= gcd;
1065  }
1066 
1067  const IntegerVariable bucket_sum =
1068  integer_trail->AddIntegerVariable(bucket_lb, bucket_ub);
1069  bucket_sum_vars.push_back(bucket_sum);
1070  bucket_sum_coeffs.push_back(gcd);
1071  local_vars.push_back(bucket_sum);
1072  local_coeffs.push_back(-1);
1073 
1074  if (lb_required) {
1075  // We have sum bucket_var >= lb, so we need local_vars >= bucket_var.
1076  m->Add(WeightedSumGreaterOrEqual(local_vars, local_coeffs, 0));
1077  }
1078  if (ub_required) {
1079  // Similarly, bucket_var <= ub, so we need local_vars <= bucket_var
1080  m->Add(WeightedSumLowerOrEqual(local_vars, local_coeffs, 0));
1081  }
1082  }
1083  *vars = bucket_sum_vars;
1084  *coeffs = bucket_sum_coeffs;
1085 }
1086 
1087 void LoadLinearConstraint(const ConstraintProto& ct, Model* m) {
1088  auto* mapping = m->GetOrCreate<CpModelMapping>();
1089  if (ct.linear().vars().empty()) {
1090  const Domain rhs = ReadDomainFromProto(ct.linear());
1091  if (rhs.Contains(0)) return;
1092  if (HasEnforcementLiteral(ct)) {
1093  std::vector<Literal> clause;
1094  for (const int ref : ct.enforcement_literal()) {
1095  clause.push_back(mapping->Literal(ref).Negated());
1096  }
1097  m->Add(ClauseConstraint(clause));
1098  } else {
1099  VLOG(1) << "Trivially UNSAT constraint: " << ct.DebugString();
1100  m->GetOrCreate<SatSolver>()->NotifyThatModelIsUnsat();
1101  }
1102  return;
1103  }
1104 
1105  if (IsPartOfProductEncoding(ct)) {
1106  const Literal l = mapping->Literal(ct.enforcement_literal(0));
1107  auto* detector = m->GetOrCreate<ProductDetector>();
1108  if (ct.linear().vars().size() == 1) {
1109  // TODO(user): Actually this should never be called since we process
1110  // linear1 in ExtractEncoding().
1111  detector->ProcessConditionalZero(l,
1112  mapping->Integer(ct.linear().vars(0)));
1113  } else if (ct.linear().vars().size() == 2) {
1114  const IntegerVariable x = mapping->Integer(ct.linear().vars(0));
1115  const IntegerVariable y = mapping->Integer(ct.linear().vars(1));
1116  detector->ProcessConditionalEquality(
1117  l, x,
1118  ct.linear().coeffs(0) == ct.linear().coeffs(1) ? NegationOf(y) : y);
1119  }
1120  }
1121 
1122  auto* integer_trail = m->GetOrCreate<IntegerTrail>();
1123  std::vector<IntegerVariable> vars = mapping->Integers(ct.linear().vars());
1124  std::vector<int64_t> coeffs = ValuesFromProto(ct.linear().coeffs());
1125 
1126  // Compute the min/max to relax the bounds if needed.
1127  //
1128  // TODO(user): Reuse ComputeLinearBounds()? but then we need another loop
1129  // to detect if we only have Booleans.
1130  IntegerValue min_sum(0);
1131  IntegerValue max_sum(0);
1132  IntegerValue max_domain_size(0);
1133  bool all_booleans = true;
1134  for (int i = 0; i < vars.size(); ++i) {
1135  if (all_booleans && !mapping->IsBoolean(ct.linear().vars(i))) {
1136  all_booleans = false;
1137  }
1138  const IntegerValue lb = integer_trail->LowerBound(vars[i]);
1139  const IntegerValue ub = integer_trail->UpperBound(vars[i]);
1140  max_domain_size = std::max(max_domain_size, ub - lb + 1);
1141  const IntegerValue term_a = coeffs[i] * lb;
1142  const IntegerValue term_b = coeffs[i] * ub;
1143  min_sum += std::min(term_a, term_b);
1144  max_sum += std::max(term_a, term_b);
1145  }
1146 
1147  const SatParameters& params = *m->GetOrCreate<SatParameters>();
1148  const IntegerValue domain_size_limit(
1149  params.max_domain_size_when_encoding_eq_neq_constraints());
1150  if (ct.linear().vars_size() == 2 && !integer_trail->IsFixed(vars[0]) &&
1151  !integer_trail->IsFixed(vars[1]) &&
1152  max_domain_size <= domain_size_limit) {
1153  auto* encoder = m->GetOrCreate<IntegerEncoder>();
1154  if (params.boolean_encoding_level() > 0 && ConstraintIsEq(ct.linear()) &&
1155  ct.linear().domain(0) != min_sum && ct.linear().domain(0) != max_sum &&
1156  encoder->VariableIsFullyEncoded(vars[0]) &&
1157  encoder->VariableIsFullyEncoded(vars[1])) {
1158  VLOG(3) << "Load AC version of " << ct.DebugString() << ", var0 domain = "
1159  << integer_trail->InitialVariableDomain(vars[0])
1160  << ", var1 domain = "
1161  << integer_trail->InitialVariableDomain(vars[1]);
1162  return LoadEquivalenceAC(mapping->Literals(ct.enforcement_literal()),
1163  IntegerValue(coeffs[0]), vars[0],
1164  IntegerValue(coeffs[1]), vars[1],
1165  IntegerValue(ct.linear().domain(0)), m);
1166  }
1167 
1168  int64_t single_value = 0;
1169  if (params.boolean_encoding_level() > 0 &&
1170  ConstraintIsNEq(ct.linear(), mapping, integer_trail, &single_value) &&
1171  single_value != min_sum && single_value != max_sum &&
1172  encoder->VariableIsFullyEncoded(vars[0]) &&
1173  encoder->VariableIsFullyEncoded(vars[1])) {
1174  VLOG(3) << "Load NAC version of " << ct.DebugString()
1175  << ", var0 domain = "
1176  << integer_trail->InitialVariableDomain(vars[0])
1177  << ", var1 domain = "
1178  << integer_trail->InitialVariableDomain(vars[1])
1179  << ", value = " << single_value;
1180  return LoadEquivalenceNeqAC(mapping->Literals(ct.enforcement_literal()),
1181  IntegerValue(coeffs[0]), vars[0],
1182  IntegerValue(coeffs[1]), vars[1],
1183  IntegerValue(single_value), m);
1184  }
1185  }
1186 
1187  // Note that the domain/enforcement of the main constraint do not change.
1188  // Same for the min/sum and max_sum. The intermediate variables are always
1189  // equal to the intermediate sum, independently of the enforcement.
1190  const bool pseudo_boolean = !HasEnforcementLiteral(ct) &&
1191  ct.linear().domain_size() == 2 && all_booleans;
1192  if (ct.linear().vars().size() > 100 && !pseudo_boolean) {
1193  const auto& domain = ct.linear().domain();
1195  domain.size() > 2 || min_sum < domain[0],
1196  domain.size() > 2 || max_sum > domain[1], &vars, &coeffs, m);
1197  }
1198 
1199  if (ct.linear().domain_size() == 2) {
1200  int64_t lb = ct.linear().domain(0);
1201  int64_t ub = ct.linear().domain(1);
1202  if (min_sum >= lb) lb = std::numeric_limits<int64_t>::min();
1203  if (max_sum <= ub) ub = std::numeric_limits<int64_t>::max();
1204 
1205  if (!HasEnforcementLiteral(ct)) {
1206  if (all_booleans) {
1207  // TODO(user): we should probably also implement an
1208  // half-reified version of this constraint.
1209  std::vector<LiteralWithCoeff> cst;
1210  for (int i = 0; i < vars.size(); ++i) {
1211  const int ref = ct.linear().vars(i);
1212  cst.push_back({mapping->Literal(ref), coeffs[i]});
1213  }
1214  m->Add(BooleanLinearConstraint(lb, ub, &cst));
1215  } else {
1216  if (lb != std::numeric_limits<int64_t>::min()) {
1217  m->Add(WeightedSumGreaterOrEqual(vars, coeffs, lb));
1218  }
1219  if (ub != std::numeric_limits<int64_t>::max()) {
1220  m->Add(WeightedSumLowerOrEqual(vars, coeffs, ub));
1221  }
1222  }
1223  } else {
1224  const std::vector<Literal> enforcement_literals =
1225  mapping->Literals(ct.enforcement_literal());
1226  if (lb != std::numeric_limits<int64_t>::min()) {
1227  m->Add(ConditionalWeightedSumGreaterOrEqual(enforcement_literals, vars,
1228  coeffs, lb));
1229  }
1230  if (ub != std::numeric_limits<int64_t>::max()) {
1231  m->Add(ConditionalWeightedSumLowerOrEqual(enforcement_literals, vars,
1232  coeffs, ub));
1233  }
1234  }
1235  } else {
1236  // In this case, we can create just one Boolean instead of two since one
1237  // is the negation of the other.
1238  const bool special_case =
1239  ct.enforcement_literal().empty() && ct.linear().domain_size() == 4;
1240 
1241  std::vector<Literal> clause;
1242  for (int i = 0; i < ct.linear().domain_size(); i += 2) {
1243  int64_t lb = ct.linear().domain(i);
1244  int64_t ub = ct.linear().domain(i + 1);
1245  if (min_sum >= lb) lb = std::numeric_limits<int64_t>::min();
1246  if (max_sum <= ub) ub = std::numeric_limits<int64_t>::max();
1247 
1248  const Literal subdomain_literal(
1249  special_case && i > 0 ? clause.back().Negated()
1250  : Literal(m->Add(NewBooleanVariable()), true));
1251  clause.push_back(subdomain_literal);
1252 
1253  if (lb != std::numeric_limits<int64_t>::min()) {
1254  m->Add(ConditionalWeightedSumGreaterOrEqual({subdomain_literal}, vars,
1255  coeffs, lb));
1256  }
1257  if (ub != std::numeric_limits<int64_t>::max()) {
1258  m->Add(ConditionalWeightedSumLowerOrEqual({subdomain_literal}, vars,
1259  coeffs, ub));
1260  }
1261  }
1262 
1263  const std::vector<Literal> enforcement_literals =
1264  mapping->Literals(ct.enforcement_literal());
1265 
1266  // Make sure all booleans are tights when enumerating all solutions.
1267  if (params.enumerate_all_solutions() && !enforcement_literals.empty()) {
1268  Literal linear_is_enforced;
1269  if (enforcement_literals.size() == 1) {
1270  linear_is_enforced = enforcement_literals[0];
1271  } else {
1272  linear_is_enforced = Literal(m->Add(NewBooleanVariable()), true);
1273  std::vector<Literal> maintain_linear_is_enforced;
1274  for (const Literal e_lit : enforcement_literals) {
1275  m->Add(Implication(e_lit.Negated(), linear_is_enforced.Negated()));
1276  maintain_linear_is_enforced.push_back(e_lit.Negated());
1277  }
1278  maintain_linear_is_enforced.push_back(linear_is_enforced);
1279  m->Add(ClauseConstraint(maintain_linear_is_enforced));
1280  }
1281  for (const Literal lit : clause) {
1282  m->Add(Implication(linear_is_enforced.Negated(), lit.Negated()));
1283  if (special_case) break; // For the unique Boolean var to be false.
1284  }
1285  }
1286 
1287  if (!special_case) {
1288  for (const Literal e_lit : enforcement_literals) {
1289  clause.push_back(e_lit.Negated());
1290  }
1291  m->Add(ClauseConstraint(clause));
1292  }
1293  }
1294 }
1295 
1296 void LoadAllDiffConstraint(const ConstraintProto& ct, Model* m) {
1297  auto* mapping = m->GetOrCreate<CpModelMapping>();
1298  const std::vector<AffineExpression> expressions =
1299  mapping->Affines(ct.all_diff().exprs());
1300  m->Add(AllDifferentOnBounds(expressions));
1301 }
1302 
1303 void LoadIntProdConstraint(const ConstraintProto& ct, Model* m) {
1304  auto* mapping = m->GetOrCreate<CpModelMapping>();
1305  const AffineExpression prod = mapping->Affine(ct.int_prod().target());
1306  CHECK_EQ(ct.int_prod().exprs_size(), 2)
1307  << "General int_prod not supported yet.";
1308 
1309  const AffineExpression expr0 = mapping->Affine(ct.int_prod().exprs(0));
1310  const AffineExpression expr1 = mapping->Affine(ct.int_prod().exprs(1));
1311  if (VLOG_IS_ON(1)) {
1312  LinearConstraintBuilder builder(m);
1313  if (DetectLinearEncodingOfProducts(expr0, expr1, m, &builder)) {
1314  VLOG(1) << "Product " << ct.DebugString() << " can be linearized";
1315  }
1316  }
1317  m->Add(ProductConstraint(expr0, expr1, prod));
1318 }
1319 
1320 void LoadIntDivConstraint(const ConstraintProto& ct, Model* m) {
1321  auto* integer_trail = m->GetOrCreate<IntegerTrail>();
1322  auto* mapping = m->GetOrCreate<CpModelMapping>();
1323  const AffineExpression div = mapping->Affine(ct.int_div().target());
1324  const AffineExpression num = mapping->Affine(ct.int_div().exprs(0));
1325  const AffineExpression denom = mapping->Affine(ct.int_div().exprs(1));
1326  if (integer_trail->IsFixed(denom)) {
1327  m->Add(FixedDivisionConstraint(num, integer_trail->FixedValue(denom), div));
1328  } else {
1329  if (VLOG_IS_ON(1)) {
1330  LinearConstraintBuilder builder(m);
1331  if (DetectLinearEncodingOfProducts(num, denom, m, &builder)) {
1332  VLOG(1) << "Division " << ct.DebugString() << " can be linearized";
1333  }
1334  }
1335  m->Add(DivisionConstraint(num, denom, div));
1336  }
1337 }
1338 
1339 void LoadIntModConstraint(const ConstraintProto& ct, Model* m) {
1340  auto* mapping = m->GetOrCreate<CpModelMapping>();
1341  auto* integer_trail = m->GetOrCreate<IntegerTrail>();
1342 
1343  const AffineExpression target = mapping->Affine(ct.int_mod().target());
1344  const AffineExpression expr = mapping->Affine(ct.int_mod().exprs(0));
1345  const AffineExpression mod = mapping->Affine(ct.int_mod().exprs(1));
1346  CHECK(integer_trail->IsFixed(mod));
1347  const IntegerValue fixed_modulo = integer_trail->FixedValue(mod);
1348  m->Add(FixedModuloConstraint(expr, fixed_modulo, target));
1349 }
1350 
1351 void LoadLinMaxConstraint(const ConstraintProto& ct, Model* m) {
1352  if (ct.lin_max().exprs().empty()) {
1353  m->GetOrCreate<SatSolver>()->NotifyThatModelIsUnsat();
1354  return;
1355  }
1356 
1357  auto* mapping = m->GetOrCreate<CpModelMapping>();
1358  const LinearExpression max = mapping->GetExprFromProto(ct.lin_max().target());
1359  std::vector<LinearExpression> negated_exprs;
1360  negated_exprs.reserve(ct.lin_max().exprs_size());
1361  for (int i = 0; i < ct.lin_max().exprs_size(); ++i) {
1362  negated_exprs.push_back(
1363  NegationOf(mapping->GetExprFromProto(ct.lin_max().exprs(i))));
1364  }
1365  // TODO(user): Consider replacing the min propagator by max.
1366  m->Add(IsEqualToMinOf(NegationOf(max), negated_exprs));
1367 }
1368 
1369 void LoadNoOverlapConstraint(const ConstraintProto& ct, Model* m) {
1370  auto* mapping = m->GetOrCreate<CpModelMapping>();
1371  auto* params = m->GetOrCreate<SatParameters>();
1372  const int num_intervals = ct.no_overlap().intervals_size();
1373  if (num_intervals <=
1374  params->max_size_to_create_precedence_literals_in_disjunctive() &&
1375  params->use_strong_propagation_in_disjunctive()) {
1377  mapping->Intervals(ct.no_overlap().intervals()), m);
1378  } else {
1379  m->Add(Disjunctive(mapping->Intervals(ct.no_overlap().intervals())));
1380  }
1381 }
1382 
1383 void LoadNoOverlap2dConstraint(const ConstraintProto& ct, Model* m) {
1384  if (ct.no_overlap_2d().x_intervals().empty()) return;
1385  auto* mapping = m->GetOrCreate<CpModelMapping>();
1386  const std::vector<IntervalVariable> x_intervals =
1387  mapping->Intervals(ct.no_overlap_2d().x_intervals());
1388  const std::vector<IntervalVariable> y_intervals =
1389  mapping->Intervals(ct.no_overlap_2d().y_intervals());
1391  x_intervals, y_intervals,
1392  !ct.no_overlap_2d().boxes_with_null_area_can_overlap()));
1393 }
1394 
1395 void LoadCumulativeConstraint(const ConstraintProto& ct, Model* m) {
1396  auto* mapping = m->GetOrCreate<CpModelMapping>();
1397  const std::vector<IntervalVariable> intervals =
1398  mapping->Intervals(ct.cumulative().intervals());
1399  const AffineExpression capacity = mapping->Affine(ct.cumulative().capacity());
1400  const std::vector<AffineExpression> demands =
1401  mapping->Affines(ct.cumulative().demands());
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  const std::vector<AffineExpression> times =
1409  mapping->Affines(ct.reservoir().time_exprs());
1410  const std::vector<AffineExpression> level_changes =
1411  mapping->Affines(ct.reservoir().level_changes());
1412  std::vector<Literal> presences;
1413  const int size = ct.reservoir().time_exprs().size();
1414  for (int i = 0; i < size; ++i) {
1415  if (!ct.reservoir().active_literals().empty()) {
1416  presences.push_back(mapping->Literal(ct.reservoir().active_literals(i)));
1417  } else {
1418  presences.push_back(encoder->GetTrueLiteral());
1419  }
1420  }
1421  AddReservoirConstraint(times, level_changes, presences,
1422  ct.reservoir().min_level(), ct.reservoir().max_level(),
1423  m);
1424 }
1425 
1426 void LoadCircuitConstraint(const ConstraintProto& ct, Model* m) {
1427  const auto& circuit = ct.circuit();
1428  if (circuit.tails().empty()) return;
1429 
1430  std::vector<int> tails(circuit.tails().begin(), circuit.tails().end());
1431  std::vector<int> heads(circuit.heads().begin(), circuit.heads().end());
1432  std::vector<Literal> literals =
1433  m->GetOrCreate<CpModelMapping>()->Literals(circuit.literals());
1434  const int num_nodes = ReindexArcs(&tails, &heads);
1435  m->Add(SubcircuitConstraint(num_nodes, tails, heads, literals));
1436 }
1437 
1438 void LoadRoutesConstraint(const ConstraintProto& ct, Model* m) {
1439  const auto& routes = ct.routes();
1440  if (routes.tails().empty()) return;
1441 
1442  std::vector<int> tails(routes.tails().begin(), routes.tails().end());
1443  std::vector<int> heads(routes.heads().begin(), routes.heads().end());
1444  std::vector<Literal> literals =
1445  m->GetOrCreate<CpModelMapping>()->Literals(routes.literals());
1446  const int num_nodes = ReindexArcs(&tails, &heads);
1447  m->Add(SubcircuitConstraint(num_nodes, tails, heads, literals,
1448  /*multiple_subcircuit_through_zero=*/true));
1449 }
1450 
1451 bool LoadConstraint(const ConstraintProto& ct, Model* m) {
1452  switch (ct.constraint_case()) {
1453  case ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET:
1454  return true;
1455  case ConstraintProto::ConstraintCase::kBoolOr:
1457  return true;
1458  case ConstraintProto::ConstraintCase::kBoolAnd:
1460  return true;
1461  case ConstraintProto::ConstraintCase::kAtMostOne:
1463  return true;
1464  case ConstraintProto::ConstraintCase::kExactlyOne:
1466  return true;
1467  case ConstraintProto::ConstraintCase::kBoolXor:
1469  return true;
1470  case ConstraintProto::ConstraintProto::kLinear:
1472  return true;
1473  case ConstraintProto::ConstraintProto::kAllDiff:
1475  return true;
1476  case ConstraintProto::ConstraintProto::kIntProd:
1478  return true;
1479  case ConstraintProto::ConstraintProto::kIntDiv:
1481  return true;
1482  case ConstraintProto::ConstraintProto::kIntMod:
1484  return true;
1485  case ConstraintProto::ConstraintProto::kLinMax:
1487  return true;
1488  case ConstraintProto::ConstraintProto::kInterval:
1489  // Already dealt with.
1490  return true;
1491  case ConstraintProto::ConstraintProto::kNoOverlap:
1493  return true;
1494  case ConstraintProto::ConstraintProto::kNoOverlap2D:
1496  return true;
1497  case ConstraintProto::ConstraintProto::kCumulative:
1499  return true;
1500  case ConstraintProto::ConstraintProto::kReservoir:
1502  return true;
1503  case ConstraintProto::ConstraintProto::kCircuit:
1505  return true;
1506  case ConstraintProto::ConstraintProto::kRoutes:
1508  return true;
1509  default:
1510  return false;
1511  }
1512 }
1513 
1514 } // namespace sat
1515 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
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.
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
std::vector< AffineExpression > Affines(const List &list) const
std::vector< IntervalVariable > Intervals(const ProtoIndices &indices) const
void ReserveSpaceForNumVariables(int num_vars)
Definition: integer.cc:797
Literal(int signed_value)
Definition: sat_base.h:74
LiteralIndex Index() const
Definition: sat_base.h:90
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
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:85
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
Definition: sat/model.h:110
void ProcessConditionalZero(Literal l, IntegerVariable p)
bool AddProblemClause(absl::Span< const Literal > literals, bool is_safe=true)
Definition: sat_solver.cc:203
int64_t b
SatParameters parameters
CpModelProto proto
CpModelProto const * model_proto
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition: stl_util.h:58
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:269
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:98
std::function< void(Model *)> NonOverlappingRectangles(const std::vector< IntervalVariable > &x, const std::vector< IntervalVariable > &y, bool is_strict)
Definition: diffn.h:98
void LoadExactlyOneConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
Definition: sat_solver.h:946
void LoadVariables(const CpModelProto &model_proto, bool view_all_booleans_as_integers, Model *m)
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
Definition: sat_solver.h:918
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 *)> ConditionalWeightedSumLowerOrEqual(const std::vector< Literal > &enforcement_literals, const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
Definition: integer_expr.h:449
void ExtractElementEncoding(const CpModelProto &model_proto, Model *m)
void AddDisjunctiveWithBooleanPrecedences(const std::vector< IntervalVariable > &intervals, Model *model)
Definition: disjunctive.cc:209
const LiteralIndex kNoLiteralIndex(-1)
std::function< void(Model *)> Disjunctive(const std::vector< IntervalVariable > &intervals)
Definition: disjunctive.cc:39
std::function< void(Model *)> LiteralXorIs(const std::vector< Literal > &literals, bool value)
std::function< BooleanVariable(Model *)> NewBooleanVariable()
Definition: integer.h:1720
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:631
bool HasEnforcementLiteral(const ConstraintProto &ct)
void LoadBooleanSymmetries(const CpModelProto &model_proto, 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:547
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)
void LoadIntModConstraint(const ConstraintProto &ct, Model *m)
const IntegerVariable kNoIntegerVariable(-1)
const IntervalVariable kNoIntervalVariable(-1)
std::function< void(Model *)> Cumulative(const std::vector< IntervalVariable > &vars, const std::vector< AffineExpression > &demands, AffineExpression capacity, SchedulingConstraintHelper *helper)
Definition: cumulative.cc:41
std::function< void(Model *)> EnforcedClause(absl::Span< const Literal > enforcement_literals, absl::Span< const Literal > clause)
Definition: sat_solver.h:986
void AddReservoirConstraint(std::vector< AffineExpression > times, std::vector< AffineExpression > deltas, std::vector< Literal > presences, int64_t min_level, int64_t max_level, Model *model)
Definition: timetable.cc:32
void LoadIntDivConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> ProductConstraint(AffineExpression a, AffineExpression b, AffineExpression p)
Definition: integer_expr.h:818
void LoadLinearConstraint(const ConstraintProto &ct, Model *m)
void SplitAndLoadIntermediateConstraints(bool lb_required, bool ub_required, std::vector< IntegerVariable > *vars, std::vector< int64_t > *coeffs, Model *m)
std::function< void(Model *)> Implication(const std::vector< Literal > &enforcement_literals, IntegerLiteral i)
Definition: integer.h:1845
bool DetectLinearEncodingOfProducts(const AffineExpression &left, const AffineExpression &right, Model *model, LinearConstraintBuilder *builder)
std::function< void(Model *)> BooleanLinearConstraint(int64_t lower_bound, int64_t upper_bound, std::vector< LiteralWithCoeff > *cst)
Definition: sat_solver.h:893
int ReindexArcs(IntContainer *tails, IntContainer *heads, absl::flat_hash_map< int, int > *mapping_output=nullptr)
Definition: circuit.h:209
std::function< void(Model *)> WeightedSumLowerOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
Definition: integer_expr.h:369
void LoadAtMostOneConstraint(const ConstraintProto &ct, Model *m)
void LoadCircuitConstraint(const ConstraintProto &ct, Model *m)
void LoadNoOverlapConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> DivisionConstraint(AffineExpression num, AffineExpression denom, AffineExpression div)
Definition: integer_expr.h:841
void DetectOptionalVariables(const CpModelProto &model_proto, Model *m)
void LoadAllDiffConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> FixedDivisionConstraint(AffineExpression a, IntegerValue b, AffineExpression c)
Definition: integer_expr.h:860
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:46
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
std::function< void(Model *)> IsEqualToMinOf(IntegerVariable min_var, const std::vector< IntegerVariable > &vars)
Definition: integer_expr.h:721
void LoadNoOverlap2dConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> AtMostOneConstraint(const std::vector< Literal > &literals)
Definition: sat_solver.h:932
IndexReferences GetReferencesUsedByConstraint(const ConstraintProto &ct)
std::function< void(Model *)> FixedModuloConstraint(AffineExpression a, IntegerValue b, AffineExpression c)
Definition: integer_expr.h:874
void AddFullEncodingFromSearchBranching(const CpModelProto &model_proto, Model *m)
void ExtractEncoding(const CpModelProto &model_proto, Model *m)
const BooleanVariable kNoBooleanVariable(-1)
void PropagateEncodingFromEquivalenceRelations(const CpModelProto &model_proto, Model *m)
std::function< std::vector< ValueLiteralPair >Model *)> FullyEncodeVariable(IntegerVariable var)
Definition: integer.h:1894
std::function< void(Model *)> AllDifferentOnBounds(const std::vector< AffineExpression > &expressions)
std::function< void(Model *)> WeightedSumGreaterOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
Definition: integer_expr.h:427
Collection of objects used to extend the Constraint Solver library.
Literal literal
Definition: optimization.cc:88
int64_t capacity
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1505
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1499
#define SOLVER_LOG(logger,...)
Definition: util/logging.h:69
#define VLOG(verboselevel)
Definition: vlog.h:39
#define VLOG_IS_ON(verboselevel)
Definition: vlog_is_on.h:47