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