OR-Tools  9.0
cp_model_checker.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 <memory>
20 #include <utility>
21 
22 #include "absl/container/flat_hash_map.h"
23 #include "absl/container/flat_hash_set.h"
24 #include "absl/strings/str_cat.h"
25 #include "ortools/base/hash.h"
26 #include "ortools/base/logging.h"
27 #include "ortools/base/map_util.h"
33 
34 namespace operations_research {
35 namespace sat {
36 namespace {
37 
38 // =============================================================================
39 // CpModelProto validation.
40 // =============================================================================
41 
42 // If the string returned by "statement" is not empty, returns it.
43 #define RETURN_IF_NOT_EMPTY(statement) \
44  do { \
45  const std::string error_message = statement; \
46  if (!error_message.empty()) return error_message; \
47  } while (false)
48 
49 template <typename ProtoWithDomain>
50 bool DomainInProtoIsValid(const ProtoWithDomain& proto) {
51  if (proto.domain().size() % 2) return false;
52  std::vector<ClosedInterval> domain;
53  for (int i = 0; i < proto.domain_size(); i += 2) {
54  if (proto.domain(i) > proto.domain(i + 1)) return false;
55  domain.push_back({proto.domain(i), proto.domain(i + 1)});
56  }
57  return IntervalsAreSortedAndNonAdjacent(domain);
58 }
59 
60 bool VariableReferenceIsValid(const CpModelProto& model, int reference) {
61  // We do it this way to avoid overflow if reference is kint64min for instance.
62  if (reference >= model.variables_size()) return false;
63  return reference >= -static_cast<int>(model.variables_size());
64 }
65 
66 bool LiteralReferenceIsValid(const CpModelProto& model, int reference) {
67  if (!VariableReferenceIsValid(model, reference)) return false;
68  const auto& var_proto = model.variables(PositiveRef(reference));
69  const int64_t min_domain = var_proto.domain(0);
70  const int64_t max_domain = var_proto.domain(var_proto.domain_size() - 1);
71  return min_domain >= 0 && max_domain <= 1;
72 }
73 
74 std::string ValidateIntegerVariable(const CpModelProto& model, int v) {
75  const IntegerVariableProto& proto = model.variables(v);
76  if (proto.domain_size() == 0) {
77  return absl::StrCat("var #", v,
78  " has no domain(): ", ProtobufShortDebugString(proto));
79  }
80  if (proto.domain_size() % 2 != 0) {
81  return absl::StrCat("var #", v, " has an odd domain() size: ",
83  }
84  if (!DomainInProtoIsValid(proto)) {
85  return absl::StrCat("var #", v, " has and invalid domain() format: ",
87  }
88 
89  // Internally, we often take the negation of a domain, and we also want to
90  // have sentinel values greater than the min/max of a variable domain, so
91  // the domain must fall in [kint64min + 2, kint64max - 1].
92  const int64_t lb = proto.domain(0);
93  const int64_t ub = proto.domain(proto.domain_size() - 1);
94  if (lb < std::numeric_limits<int64_t>::min() + 2 ||
96  return absl::StrCat(
97  "var #", v, " domain do not fall in [kint64min + 2, kint64max - 1]. ",
99  }
100 
101  // We do compute ub - lb in some place in the code and do not want to deal
102  // with overflow everywhere. This seems like a reasonable precondition anyway.
103  if (lb < 0 && lb + std::numeric_limits<int64_t>::max() < ub) {
104  return absl::StrCat(
105  "var #", v,
106  " has a domain that is too large, i.e. |UB - LB| overflow an int64_t: ",
108  }
109 
110  return "";
111 }
112 
113 std::string ValidateArgumentReferencesInConstraint(const CpModelProto& model,
114  int c) {
115  const ConstraintProto& ct = model.constraints(c);
116  IndexReferences references = GetReferencesUsedByConstraint(ct);
117  for (const int v : references.variables) {
118  if (!VariableReferenceIsValid(model, v)) {
119  return absl::StrCat("Out of bound integer variable ", v,
120  " in constraint #", c, " : ",
122  }
123  }
124  for (const int lit : ct.enforcement_literal()) {
125  if (!LiteralReferenceIsValid(model, lit)) {
126  return absl::StrCat("Invalid enforcement literal ", lit,
127  " in constraint #", c, " : ",
129  }
130  }
131  for (const int lit : references.literals) {
132  if (!LiteralReferenceIsValid(model, lit)) {
133  return absl::StrCat("Invalid literal ", lit, " in constraint #", c, " : ",
135  }
136  }
137  for (const int i : UsedIntervals(ct)) {
138  if (i < 0 || i >= model.constraints_size()) {
139  return absl::StrCat("Out of bound interval ", i, " in constraint #", c,
140  " : ", ProtobufShortDebugString(ct));
141  }
142  if (model.constraints(i).constraint_case() !=
143  ConstraintProto::ConstraintCase::kInterval) {
144  return absl::StrCat(
145  "Interval ", i,
146  " does not refer to an interval constraint. Problematic constraint #",
147  c, " : ", ProtobufShortDebugString(ct));
148  }
149  }
150  return "";
151 }
152 
153 template <class LinearExpressionProto>
154 bool PossibleIntegerOverflow(const CpModelProto& model,
155  const LinearExpressionProto& proto) {
156  int64_t sum_min = 0;
157  int64_t sum_max = 0;
158  for (int i = 0; i < proto.vars_size(); ++i) {
159  const int ref = proto.vars(i);
160  const auto& var_proto = model.variables(PositiveRef(ref));
161  const int64_t min_domain = var_proto.domain(0);
162  const int64_t max_domain = var_proto.domain(var_proto.domain_size() - 1);
163  if (proto.coeffs(i) == std::numeric_limits<int64_t>::min()) return true;
164  const int64_t coeff =
165  RefIsPositive(ref) ? proto.coeffs(i) : -proto.coeffs(i);
166  const int64_t prod1 = CapProd(min_domain, coeff);
167  const int64_t prod2 = CapProd(max_domain, coeff);
168 
169  // Note that we use min/max with zero to disallow "alternative" terms and
170  // be sure that we cannot have an overflow if we do the computation in a
171  // different order.
172  sum_min = CapAdd(sum_min, std::min(int64_t{0}, std::min(prod1, prod2)));
173  sum_max = CapAdd(sum_max, std::max(int64_t{0}, std::max(prod1, prod2)));
174  for (const int64_t v : {prod1, prod2, sum_min, sum_max}) {
177  return true;
178  }
179  }
180 
181  // In addition to computing the min/max possible sum, we also often compare
182  // it with the constraint bounds, so we do not want max - min to overflow.
183  if (sum_min < 0 && sum_min + std::numeric_limits<int64_t>::max() < sum_max) {
184  return true;
185  }
186  return false;
187 }
188 
189 std::string ValidateLinearExpression(const CpModelProto& model,
190  const LinearExpressionProto& expr) {
191  if (expr.coeffs_size() != expr.vars_size()) {
192  return absl::StrCat("coeffs_size() != vars_size() in linear expression: ",
194  }
195  if (PossibleIntegerOverflow(model, expr)) {
196  return absl::StrCat("Possible overflow in linear expression: ",
198  }
199  return "";
200 }
201 
202 std::string ValidateIntervalConstraint(const CpModelProto& model,
203  const ConstraintProto& ct) {
204  const IntervalConstraintProto& arg = ct.interval();
205  int num_view = 0;
206  if (arg.has_start_view()) {
207  ++num_view;
208  RETURN_IF_NOT_EMPTY(ValidateLinearExpression(model, arg.start_view()));
209  }
210  if (arg.has_size_view()) {
211  ++num_view;
212  RETURN_IF_NOT_EMPTY(ValidateLinearExpression(model, arg.size_view()));
213  }
214  if (arg.has_end_view()) {
215  ++num_view;
216  RETURN_IF_NOT_EMPTY(ValidateLinearExpression(model, arg.end_view()));
217  }
218  if (num_view != 0 && num_view != 3) {
219  return absl::StrCat(
220  "Interval must use either the var or the view representation, but not "
221  "both: ",
223  }
224  if (num_view > 0) return "";
225  if (arg.size() < 0) {
226  const IntegerVariableProto& size_var_proto =
227  model.variables(NegatedRef(arg.size()));
228  if (size_var_proto.domain(size_var_proto.domain_size() - 1) > 0) {
229  return absl::StrCat(
230  "Negative value in interval size domain: ", ProtobufDebugString(ct),
231  "negation of size var: ", ProtobufDebugString(size_var_proto));
232  }
233  } else {
234  const IntegerVariableProto& size_var_proto = model.variables(arg.size());
235  if (size_var_proto.domain(0) < 0) {
236  return absl::StrCat(
237  "Negative value in interval size domain: ", ProtobufDebugString(ct),
238  "size var: ", ProtobufDebugString(size_var_proto));
239  }
240  }
241  return "";
242 }
243 
244 std::string ValidateLinearConstraint(const CpModelProto& model,
245  const ConstraintProto& ct) {
246  const LinearConstraintProto& arg = ct.linear();
247  if (PossibleIntegerOverflow(model, arg)) {
248  return "Possible integer overflow in constraint: " +
250  }
251  return "";
252 }
253 
254 std::string ValidateTableConstraint(const CpModelProto& model,
255  const ConstraintProto& ct) {
256  const TableConstraintProto& arg = ct.table();
257  if (arg.vars().empty()) return "";
258  if (arg.values().size() % arg.vars().size() != 0) {
259  return absl::StrCat(
260  "The flat encoding of a table constraint must be a multiple of the "
261  "number of variable: ",
263  }
264  return "";
265 }
266 
267 std::string ValidateCircuitConstraint(const CpModelProto& model,
268  const ConstraintProto& ct) {
269  const int size = ct.circuit().tails().size();
270  if (ct.circuit().heads().size() != size ||
271  ct.circuit().literals().size() != size) {
272  return absl::StrCat("Wrong field sizes in circuit: ",
274  }
275  return "";
276 }
277 
278 std::string ValidateRoutesConstraint(const CpModelProto& model,
279  const ConstraintProto& ct) {
280  const int size = ct.routes().tails().size();
281  if (ct.routes().heads().size() != size ||
282  ct.routes().literals().size() != size) {
283  return absl::StrCat("Wrong field sizes in routes: ",
285  }
286  return "";
287 }
288 
289 std::string ValidateNoOverlap2DConstraint(const CpModelProto& model,
290  const ConstraintProto& ct) {
291  const int size_x = ct.no_overlap_2d().x_intervals().size();
292  const int size_y = ct.no_overlap_2d().y_intervals().size();
293  if (size_x != size_y) {
294  return absl::StrCat("The two lists of intervals must have the same size: ",
296  }
297  return "";
298 }
299 
300 std::string ValidateAutomatonConstraint(const CpModelProto& model,
301  const ConstraintProto& ct) {
302  const int num_transistions = ct.automaton().transition_tail().size();
303  if (num_transistions != ct.automaton().transition_head().size() ||
304  num_transistions != ct.automaton().transition_label().size()) {
305  return absl::StrCat(
306  "The transitions repeated fields must have the same size: ",
308  }
309  return "";
310 }
311 
312 std::string ValidateReservoirConstraint(const CpModelProto& model,
313  const ConstraintProto& ct) {
314  if (ct.enforcement_literal_size() > 0) {
315  return "Reservoir does not support enforcement literals.";
316  }
317  if (ct.reservoir().times().size() != ct.reservoir().demands().size()) {
318  return absl::StrCat("Times and demands fields must be of the same size: ",
320  }
321  if (ct.reservoir().min_level() > 0) {
322  return absl::StrCat(
323  "The min level of a reservoir must be <= 0. Please use fixed events to "
324  "setup initial state: ",
326  }
327  if (ct.reservoir().max_level() < 0) {
328  return absl::StrCat(
329  "The max level of a reservoir must be >= 0. Please use fixed events to "
330  "setup initial state: ",
332  }
333 
334  int64_t sum_abs = 0;
335  for (const int64_t demand : ct.reservoir().demands()) {
336  sum_abs = CapAdd(sum_abs, std::abs(demand));
337  if (sum_abs == std::numeric_limits<int64_t>::max()) {
338  return "Possible integer overflow in constraint: " +
340  }
341  }
342  if (ct.reservoir().actives_size() > 0 &&
343  ct.reservoir().actives_size() != ct.reservoir().times_size()) {
344  return "Wrong array length of actives variables";
345  }
346  if (ct.reservoir().demands_size() > 0 &&
347  ct.reservoir().demands_size() != ct.reservoir().times_size()) {
348  return "Wrong array length of demands variables";
349  }
350  return "";
351 }
352 
353 std::string ValidateIntModConstraint(const CpModelProto& model,
354  const ConstraintProto& ct) {
355  if (ct.int_mod().vars().size() != 2) {
356  return absl::StrCat("An int_mod constraint should have exactly 2 terms: ",
358  }
359  const int mod_var = ct.int_mod().vars(1);
360  const IntegerVariableProto& mod_proto = model.variables(PositiveRef(mod_var));
361  if ((RefIsPositive(mod_var) && mod_proto.domain(0) <= 0) ||
362  (!RefIsPositive(mod_var) && mod_proto.domain(0) >= 0)) {
363  return absl::StrCat(
364  "An int_mod must have a strictly positive modulo argument: ",
366  }
367  return "";
368 }
369 
370 std::string ValidateIntDivConstraint(const CpModelProto& model,
371  const ConstraintProto& ct) {
372  if (ct.int_div().vars().size() != 2) {
373  return absl::StrCat("An int_div constraint should have exactly 2 terms: ",
375  }
376  return "";
377 }
378 
379 std::string ValidateObjective(const CpModelProto& model,
380  const CpObjectiveProto& obj) {
381  if (!DomainInProtoIsValid(obj)) {
382  return absl::StrCat("The objective has and invalid domain() format: ",
384  }
385  if (obj.vars().size() != obj.coeffs().size()) {
386  return absl::StrCat("vars and coeffs size do not match in objective: ",
388  }
389  for (const int v : obj.vars()) {
390  if (!VariableReferenceIsValid(model, v)) {
391  return absl::StrCat("Out of bound integer variable ", v,
392  " in objective: ", ProtobufShortDebugString(obj));
393  }
394  }
395  if (PossibleIntegerOverflow(model, obj)) {
396  return "Possible integer overflow in objective: " +
397  ProtobufDebugString(obj);
398  }
399  return "";
400 }
401 
402 std::string ValidateSearchStrategies(const CpModelProto& model) {
403  for (const DecisionStrategyProto& strategy : model.search_strategy()) {
404  const int vss = strategy.variable_selection_strategy();
405  if (vss != DecisionStrategyProto::CHOOSE_FIRST &&
406  vss != DecisionStrategyProto::CHOOSE_LOWEST_MIN &&
407  vss != DecisionStrategyProto::CHOOSE_HIGHEST_MAX &&
408  vss != DecisionStrategyProto::CHOOSE_MIN_DOMAIN_SIZE &&
409  vss != DecisionStrategyProto::CHOOSE_MAX_DOMAIN_SIZE) {
410  return absl::StrCat(
411  "Unknown or unsupported variable_selection_strategy: ", vss);
412  }
413  const int drs = strategy.domain_reduction_strategy();
414  if (drs != DecisionStrategyProto::SELECT_MIN_VALUE &&
415  drs != DecisionStrategyProto::SELECT_MAX_VALUE &&
416  drs != DecisionStrategyProto::SELECT_LOWER_HALF &&
417  drs != DecisionStrategyProto::SELECT_UPPER_HALF &&
418  drs != DecisionStrategyProto::SELECT_MEDIAN_VALUE) {
419  return absl::StrCat("Unknown or unsupported domain_reduction_strategy: ",
420  drs);
421  }
422  for (const int ref : strategy.variables()) {
423  if (!VariableReferenceIsValid(model, ref)) {
424  return absl::StrCat("Invalid variable reference in strategy: ",
425  ProtobufShortDebugString(strategy));
426  }
427  }
428  int previous_index = -1;
429  for (const auto& transformation : strategy.transformations()) {
430  if (transformation.positive_coeff() <= 0) {
431  return absl::StrCat("Affine transformation coeff should be positive: ",
432  ProtobufShortDebugString(transformation));
433  }
434  if (transformation.index() <= previous_index ||
435  transformation.index() >= strategy.variables_size()) {
436  return absl::StrCat(
437  "Invalid indices (must be sorted and valid) in transformation: ",
438  ProtobufShortDebugString(transformation));
439  }
440  previous_index = transformation.index();
441  }
442  }
443  return "";
444 }
445 
446 std::string ValidateSolutionHint(const CpModelProto& model) {
447  if (!model.has_solution_hint()) return "";
448  const auto& hint = model.solution_hint();
449  if (hint.vars().size() != hint.values().size()) {
450  return "Invalid solution hint: vars and values do not have the same size.";
451  }
452  for (const int ref : hint.vars()) {
453  if (!VariableReferenceIsValid(model, ref)) {
454  return absl::StrCat("Invalid variable reference in solution hint: ", ref);
455  }
456  }
457  return "";
458 }
459 
460 } // namespace
461 
462 std::string ValidateCpModel(const CpModelProto& model) {
463  for (int v = 0; v < model.variables_size(); ++v) {
464  RETURN_IF_NOT_EMPTY(ValidateIntegerVariable(model, v));
465  }
466  for (int c = 0; c < model.constraints_size(); ++c) {
467  RETURN_IF_NOT_EMPTY(ValidateArgumentReferencesInConstraint(model, c));
468 
469  // By default, a constraint does not support enforcement literals except if
470  // explicitly stated by setting this to true below.
471  bool support_enforcement = false;
472 
473  // Other non-generic validations.
474  // TODO(user): validate all constraints.
475  const ConstraintProto& ct = model.constraints(c);
476  const ConstraintProto::ConstraintCase type = ct.constraint_case();
477  switch (type) {
478  case ConstraintProto::ConstraintCase::kIntDiv:
479  RETURN_IF_NOT_EMPTY(ValidateIntDivConstraint(model, ct));
480  break;
481  case ConstraintProto::ConstraintCase::kIntMod:
482  RETURN_IF_NOT_EMPTY(ValidateIntModConstraint(model, ct));
483  break;
484  case ConstraintProto::ConstraintCase::kTable:
485  RETURN_IF_NOT_EMPTY(ValidateTableConstraint(model, ct));
486  break;
487  case ConstraintProto::ConstraintCase::kBoolOr:
488  support_enforcement = true;
489  break;
490  case ConstraintProto::ConstraintCase::kBoolAnd:
491  support_enforcement = true;
492  break;
493  case ConstraintProto::ConstraintCase::kLinear:
494  support_enforcement = true;
495  if (!DomainInProtoIsValid(ct.linear())) {
496  return absl::StrCat("Invalid domain in constraint #", c, " : ",
498  }
499  if (ct.linear().coeffs_size() != ct.linear().vars_size()) {
500  return absl::StrCat("coeffs_size() != vars_size() in constraint #", c,
501  " : ", ProtobufShortDebugString(ct));
502  }
503  RETURN_IF_NOT_EMPTY(ValidateLinearConstraint(model, ct));
504  break;
505  case ConstraintProto::ConstraintCase::kLinMax: {
506  const std::string target_error =
507  ValidateLinearExpression(model, ct.lin_max().target());
508  if (!target_error.empty()) return target_error;
509  for (int i = 0; i < ct.lin_max().exprs_size(); ++i) {
510  const std::string expr_error =
511  ValidateLinearExpression(model, ct.lin_max().exprs(i));
512  if (!expr_error.empty()) return expr_error;
513  }
514  break;
515  }
516  case ConstraintProto::ConstraintCase::kLinMin: {
517  const std::string target_error =
518  ValidateLinearExpression(model, ct.lin_min().target());
519  if (!target_error.empty()) return target_error;
520  for (int i = 0; i < ct.lin_min().exprs_size(); ++i) {
521  const std::string expr_error =
522  ValidateLinearExpression(model, ct.lin_min().exprs(i));
523  if (!expr_error.empty()) return expr_error;
524  }
525  break;
526  }
527  case ConstraintProto::ConstraintCase::kInterval:
528  support_enforcement = true;
529  RETURN_IF_NOT_EMPTY(ValidateIntervalConstraint(model, ct));
530  break;
531  case ConstraintProto::ConstraintCase::kCumulative:
532  if (ct.cumulative().intervals_size() !=
533  ct.cumulative().demands_size()) {
534  return absl::StrCat(
535  "intervals_size() != demands_size() in constraint #", c, " : ",
537  }
538  break;
539  case ConstraintProto::ConstraintCase::kInverse:
540  if (ct.inverse().f_direct().size() != ct.inverse().f_inverse().size()) {
541  return absl::StrCat("Non-matching fields size in inverse: ",
543  }
544  break;
545  case ConstraintProto::ConstraintCase::kAutomaton:
546  RETURN_IF_NOT_EMPTY(ValidateAutomatonConstraint(model, ct));
547  break;
548  case ConstraintProto::ConstraintCase::kCircuit:
549  RETURN_IF_NOT_EMPTY(ValidateCircuitConstraint(model, ct));
550  break;
551  case ConstraintProto::ConstraintCase::kRoutes:
552  RETURN_IF_NOT_EMPTY(ValidateRoutesConstraint(model, ct));
553  break;
554  case ConstraintProto::ConstraintCase::kNoOverlap2D:
555  RETURN_IF_NOT_EMPTY(ValidateNoOverlap2DConstraint(model, ct));
556  break;
557  case ConstraintProto::ConstraintCase::kReservoir:
558  RETURN_IF_NOT_EMPTY(ValidateReservoirConstraint(model, ct));
559  break;
560  default:
561  break;
562  }
563 
564  // Because some client set fixed enforcement literal which are supported
565  // in the presolve for all constraints, we just check that there is no
566  // non-fixed enforcement.
567  if (!support_enforcement && !ct.enforcement_literal().empty()) {
568  for (const int ref : ct.enforcement_literal()) {
569  const int var = PositiveRef(ref);
570  const Domain domain = ReadDomainFromProto(model.variables(var));
571  if (domain.Size() != 1) {
572  return absl::StrCat(
573  "Enforcement literal not supported in constraint: ",
575  }
576  }
577  }
578  }
579  if (model.has_objective()) {
580  RETURN_IF_NOT_EMPTY(ValidateObjective(model, model.objective()));
581  }
582  RETURN_IF_NOT_EMPTY(ValidateSearchStrategies(model));
583  RETURN_IF_NOT_EMPTY(ValidateSolutionHint(model));
584  for (const int ref : model.assumptions()) {
585  if (!LiteralReferenceIsValid(model, ref)) {
586  return absl::StrCat("Invalid literal reference ", ref,
587  " in the 'assumptions' field.");
588  }
589  }
590  return "";
591 }
592 
593 #undef RETURN_IF_NOT_EMPTY
594 
595 // =============================================================================
596 // Solution Feasibility.
597 // =============================================================================
598 
599 namespace {
600 
601 class ConstraintChecker {
602  public:
603  explicit ConstraintChecker(const std::vector<int64_t>& variable_values)
604  : variable_values_(variable_values) {}
605 
606  bool LiteralIsTrue(int l) const {
607  if (l >= 0) return variable_values_[l] != 0;
608  return variable_values_[-l - 1] == 0;
609  }
610 
611  bool LiteralIsFalse(int l) const { return !LiteralIsTrue(l); }
612 
613  int64_t Value(int var) const {
614  if (var >= 0) return variable_values_[var];
615  return -variable_values_[-var - 1];
616  }
617 
618  bool ConstraintIsEnforced(const ConstraintProto& ct) {
619  for (const int lit : ct.enforcement_literal()) {
620  if (LiteralIsFalse(lit)) return false;
621  }
622  return true;
623  }
624 
625  bool BoolOrConstraintIsFeasible(const ConstraintProto& ct) {
626  for (const int lit : ct.bool_or().literals()) {
627  if (LiteralIsTrue(lit)) return true;
628  }
629  return false;
630  }
631 
632  bool BoolAndConstraintIsFeasible(const ConstraintProto& ct) {
633  for (const int lit : ct.bool_and().literals()) {
634  if (LiteralIsFalse(lit)) return false;
635  }
636  return true;
637  }
638 
639  bool AtMostOneConstraintIsFeasible(const ConstraintProto& ct) {
640  int num_true_literals = 0;
641  for (const int lit : ct.at_most_one().literals()) {
642  if (LiteralIsTrue(lit)) ++num_true_literals;
643  }
644  return num_true_literals <= 1;
645  }
646 
647  bool ExactlyOneConstraintIsFeasible(const ConstraintProto& ct) {
648  int num_true_literals = 0;
649  for (const int lit : ct.exactly_one().literals()) {
650  if (LiteralIsTrue(lit)) ++num_true_literals;
651  }
652  return num_true_literals == 1;
653  }
654 
655  bool BoolXorConstraintIsFeasible(const ConstraintProto& ct) {
656  int sum = 0;
657  for (const int lit : ct.bool_xor().literals()) {
658  sum ^= LiteralIsTrue(lit) ? 1 : 0;
659  }
660  return sum == 1;
661  }
662 
663  bool LinearConstraintIsFeasible(const ConstraintProto& ct) {
664  int64_t sum = 0;
665  const int num_variables = ct.linear().coeffs_size();
666  for (int i = 0; i < num_variables; ++i) {
667  sum += Value(ct.linear().vars(i)) * ct.linear().coeffs(i);
668  }
669  return DomainInProtoContains(ct.linear(), sum);
670  }
671 
672  bool IntMaxConstraintIsFeasible(const ConstraintProto& ct) {
673  const int64_t max = Value(ct.int_max().target());
674  int64_t actual_max = std::numeric_limits<int64_t>::min();
675  for (int i = 0; i < ct.int_max().vars_size(); ++i) {
676  actual_max = std::max(actual_max, Value(ct.int_max().vars(i)));
677  }
678  return max == actual_max;
679  }
680 
681  int64_t LinearExpressionValue(const LinearExpressionProto& expr) const {
682  int64_t sum = expr.offset();
683  const int num_variables = expr.vars_size();
684  for (int i = 0; i < num_variables; ++i) {
685  sum += Value(expr.vars(i)) * expr.coeffs(i);
686  }
687  return sum;
688  }
689 
690  bool LinMaxConstraintIsFeasible(const ConstraintProto& ct) {
691  const int64_t max = LinearExpressionValue(ct.lin_max().target());
692  int64_t actual_max = std::numeric_limits<int64_t>::min();
693  for (int i = 0; i < ct.lin_max().exprs_size(); ++i) {
694  const int64_t expr_value = LinearExpressionValue(ct.lin_max().exprs(i));
695  actual_max = std::max(actual_max, expr_value);
696  }
697  return max == actual_max;
698  }
699 
700  bool IntProdConstraintIsFeasible(const ConstraintProto& ct) {
701  const int64_t prod = Value(ct.int_prod().target());
702  int64_t actual_prod = 1;
703  for (int i = 0; i < ct.int_prod().vars_size(); ++i) {
704  actual_prod *= Value(ct.int_prod().vars(i));
705  }
706  return prod == actual_prod;
707  }
708 
709  bool IntDivConstraintIsFeasible(const ConstraintProto& ct) {
710  return Value(ct.int_div().target()) ==
711  Value(ct.int_div().vars(0)) / Value(ct.int_div().vars(1));
712  }
713 
714  bool IntModConstraintIsFeasible(const ConstraintProto& ct) {
715  return Value(ct.int_mod().target()) ==
716  Value(ct.int_mod().vars(0)) % Value(ct.int_mod().vars(1));
717  }
718 
719  bool IntMinConstraintIsFeasible(const ConstraintProto& ct) {
720  const int64_t min = Value(ct.int_min().target());
721  int64_t actual_min = std::numeric_limits<int64_t>::max();
722  for (int i = 0; i < ct.int_min().vars_size(); ++i) {
723  actual_min = std::min(actual_min, Value(ct.int_min().vars(i)));
724  }
725  return min == actual_min;
726  }
727 
728  bool LinMinConstraintIsFeasible(const ConstraintProto& ct) {
729  const int64_t min = LinearExpressionValue(ct.lin_min().target());
730  int64_t actual_min = std::numeric_limits<int64_t>::max();
731  for (int i = 0; i < ct.lin_min().exprs_size(); ++i) {
732  const int64_t expr_value = LinearExpressionValue(ct.lin_min().exprs(i));
733  actual_min = std::min(actual_min, expr_value);
734  }
735  return min == actual_min;
736  }
737 
738  bool AllDiffConstraintIsFeasible(const ConstraintProto& ct) {
739  absl::flat_hash_set<int64_t> values;
740  for (const int v : ct.all_diff().vars()) {
741  if (gtl::ContainsKey(values, Value(v))) return false;
742  values.insert(Value(v));
743  }
744  return true;
745  }
746 
747  int64_t IntervalStart(const IntervalConstraintProto& interval) const {
748  return interval.has_start_view()
749  ? LinearExpressionValue(interval.start_view())
750  : Value(interval.start());
751  }
752 
753  int64_t IntervalSize(const IntervalConstraintProto& interval) const {
754  return interval.has_size_view()
755  ? LinearExpressionValue(interval.size_view())
756  : Value(interval.size());
757  }
758 
759  int64_t IntervalEnd(const IntervalConstraintProto& interval) const {
760  return interval.has_end_view() ? LinearExpressionValue(interval.end_view())
761  : Value(interval.end());
762  }
763 
764  bool IntervalConstraintIsFeasible(const ConstraintProto& ct) {
765  const int64_t size = IntervalSize(ct.interval());
766  if (size < 0) return false;
767  return IntervalStart(ct.interval()) + size == IntervalEnd(ct.interval());
768  }
769 
770  bool NoOverlapConstraintIsFeasible(const CpModelProto& model,
771  const ConstraintProto& ct) {
772  std::vector<std::pair<int64_t, int64_t>> start_durations_pairs;
773  for (const int i : ct.no_overlap().intervals()) {
774  const ConstraintProto& interval_constraint = model.constraints(i);
775  if (ConstraintIsEnforced(interval_constraint)) {
776  const IntervalConstraintProto& interval =
777  interval_constraint.interval();
778  start_durations_pairs.push_back(
779  {IntervalStart(interval), IntervalSize(interval)});
780  }
781  }
782  std::sort(start_durations_pairs.begin(), start_durations_pairs.end());
783  int64_t previous_end = std::numeric_limits<int64_t>::min();
784  for (const auto pair : start_durations_pairs) {
785  if (pair.first < previous_end) return false;
786  previous_end = pair.first + pair.second;
787  }
788  return true;
789  }
790 
791  bool IntervalsAreDisjoint(const IntervalConstraintProto& interval1,
792  const IntervalConstraintProto& interval2) {
793  return IntervalEnd(interval1) <= IntervalStart(interval2) ||
794  IntervalEnd(interval2) <= IntervalStart(interval1);
795  }
796 
797  bool IntervalIsEmpty(const IntervalConstraintProto& interval) {
798  return IntervalStart(interval) == IntervalEnd(interval);
799  }
800 
801  bool NoOverlap2DConstraintIsFeasible(const CpModelProto& model,
802  const ConstraintProto& ct) {
803  const auto& arg = ct.no_overlap_2d();
804  // Those intervals from arg.x_intervals and arg.y_intervals where both
805  // the x and y intervals are enforced.
806  std::vector<std::pair<const IntervalConstraintProto* const,
807  const IntervalConstraintProto* const>>
808  enforced_intervals_xy;
809  {
810  const int num_intervals = arg.x_intervals_size();
811  CHECK_EQ(arg.y_intervals_size(), num_intervals);
812  for (int i = 0; i < num_intervals; ++i) {
813  const ConstraintProto& x = model.constraints(arg.x_intervals(i));
814  const ConstraintProto& y = model.constraints(arg.y_intervals(i));
815  if (ConstraintIsEnforced(x) && ConstraintIsEnforced(y) &&
816  (!arg.boxes_with_null_area_can_overlap() ||
817  (!IntervalIsEmpty(x.interval()) &&
818  !IntervalIsEmpty(y.interval())))) {
819  enforced_intervals_xy.push_back({&x.interval(), &y.interval()});
820  }
821  }
822  }
823  const int num_enforced_intervals = enforced_intervals_xy.size();
824  for (int i = 0; i < num_enforced_intervals; ++i) {
825  for (int j = i + 1; j < num_enforced_intervals; ++j) {
826  const auto& xi = *enforced_intervals_xy[i].first;
827  const auto& yi = *enforced_intervals_xy[i].second;
828  const auto& xj = *enforced_intervals_xy[j].first;
829  const auto& yj = *enforced_intervals_xy[j].second;
830  if (!IntervalsAreDisjoint(xi, xj) && !IntervalsAreDisjoint(yi, yj) &&
831  !IntervalIsEmpty(xi) && !IntervalIsEmpty(xj) &&
832  !IntervalIsEmpty(yi) && !IntervalIsEmpty(yj)) {
833  VLOG(1) << "Interval " << i << "(x=[" << IntervalStart(xi) << ", "
834  << IntervalEnd(xi) << "], y=[" << IntervalStart(yi) << ", "
835  << IntervalEnd(yi) << "]) and " << j << "(x=["
836  << IntervalStart(xj) << ", " << IntervalEnd(xj) << "], y=["
837  << IntervalStart(yj) << ", " << IntervalEnd(yj)
838  << "]) are not disjoint.";
839  return false;
840  }
841  }
842  }
843  return true;
844  }
845 
846  bool CumulativeConstraintIsFeasible(const CpModelProto& model,
847  const ConstraintProto& ct) {
848  // TODO(user,user): Improve complexity for large durations.
849  const int64_t capacity = Value(ct.cumulative().capacity());
850  const int num_intervals = ct.cumulative().intervals_size();
851  absl::flat_hash_map<int64_t, int64_t> usage;
852  for (int i = 0; i < num_intervals; ++i) {
853  const ConstraintProto& interval_constraint =
854  model.constraints(ct.cumulative().intervals(i));
855  if (ConstraintIsEnforced(interval_constraint)) {
856  const IntervalConstraintProto& interval =
857  interval_constraint.interval();
858  const int64_t start = IntervalStart(interval);
859  const int64_t duration = IntervalSize(interval);
860  const int64_t demand = Value(ct.cumulative().demands(i));
861  for (int64_t t = start; t < start + duration; ++t) {
862  usage[t] += demand;
863  if (usage[t] > capacity) return false;
864  }
865  }
866  }
867  return true;
868  }
869 
870  bool ElementConstraintIsFeasible(const ConstraintProto& ct) {
871  const int index = Value(ct.element().index());
872  return Value(ct.element().vars(index)) == Value(ct.element().target());
873  }
874 
875  bool TableConstraintIsFeasible(const ConstraintProto& ct) {
876  const int size = ct.table().vars_size();
877  if (size == 0) return true;
878  for (int row_start = 0; row_start < ct.table().values_size();
879  row_start += size) {
880  int i = 0;
881  while (Value(ct.table().vars(i)) == ct.table().values(row_start + i)) {
882  ++i;
883  if (i == size) return !ct.table().negated();
884  }
885  }
886  return ct.table().negated();
887  }
888 
889  bool AutomatonConstraintIsFeasible(const ConstraintProto& ct) {
890  // Build the transition table {tail, label} -> head.
891  absl::flat_hash_map<std::pair<int64_t, int64_t>, int64_t> transition_map;
892  const int num_transitions = ct.automaton().transition_tail().size();
893  for (int i = 0; i < num_transitions; ++i) {
894  transition_map[{ct.automaton().transition_tail(i),
895  ct.automaton().transition_label(i)}] =
896  ct.automaton().transition_head(i);
897  }
898 
899  // Walk the automaton.
900  int64_t current_state = ct.automaton().starting_state();
901  const int num_steps = ct.automaton().vars_size();
902  for (int i = 0; i < num_steps; ++i) {
903  const std::pair<int64_t, int64_t> key = {current_state,
904  Value(ct.automaton().vars(i))};
905  if (!gtl::ContainsKey(transition_map, key)) {
906  return false;
907  }
908  current_state = transition_map[key];
909  }
910 
911  // Check we are now in a final state.
912  for (const int64_t final : ct.automaton().final_states()) {
913  if (current_state == final) return true;
914  }
915  return false;
916  }
917 
918  bool CircuitConstraintIsFeasible(const ConstraintProto& ct) {
919  // Compute the set of relevant nodes for the constraint and set the next of
920  // each of them. This also detects duplicate nexts.
921  const int num_arcs = ct.circuit().tails_size();
922  absl::flat_hash_set<int> nodes;
923  absl::flat_hash_map<int, int> nexts;
924  for (int i = 0; i < num_arcs; ++i) {
925  const int tail = ct.circuit().tails(i);
926  const int head = ct.circuit().heads(i);
927  nodes.insert(tail);
928  nodes.insert(head);
929  if (LiteralIsFalse(ct.circuit().literals(i))) continue;
930  if (nexts.contains(tail)) return false; // Duplicate.
931  nexts[tail] = head;
932  }
933 
934  // All node must have a next.
935  int in_cycle;
936  int cycle_size = 0;
937  for (const int node : nodes) {
938  if (!nexts.contains(node)) return false; // No next.
939  if (nexts[node] == node) continue; // skip self-loop.
940  in_cycle = node;
941  ++cycle_size;
942  }
943  if (cycle_size == 0) return true;
944 
945  // Check that we have only one cycle. visited is used to not loop forever if
946  // we have a "rho" shape instead of a cycle.
947  absl::flat_hash_set<int> visited;
948  int current = in_cycle;
949  int num_visited = 0;
950  while (!visited.contains(current)) {
951  ++num_visited;
952  visited.insert(current);
953  current = nexts[current];
954  }
955  if (current != in_cycle) return false; // Rho shape.
956  return num_visited == cycle_size; // Another cycle somewhere if false.
957  }
958 
959  bool RoutesConstraintIsFeasible(const ConstraintProto& ct) {
960  const int num_arcs = ct.routes().tails_size();
961  int num_used_arcs = 0;
962  int num_self_arcs = 0;
963  int num_nodes = 0;
964  std::vector<int> tail_to_head;
965  std::vector<int> depot_nexts;
966  for (int i = 0; i < num_arcs; ++i) {
967  const int tail = ct.routes().tails(i);
968  const int head = ct.routes().heads(i);
969  num_nodes = std::max(num_nodes, 1 + tail);
970  num_nodes = std::max(num_nodes, 1 + head);
971  tail_to_head.resize(num_nodes, -1);
972  if (LiteralIsTrue(ct.routes().literals(i))) {
973  if (tail == head) {
974  if (tail == 0) return false;
975  ++num_self_arcs;
976  continue;
977  }
978  ++num_used_arcs;
979  if (tail == 0) {
980  depot_nexts.push_back(head);
981  } else {
982  if (tail_to_head[tail] != -1) return false;
983  tail_to_head[tail] = head;
984  }
985  }
986  }
987 
988  // An empty constraint with no node to visit should be feasible.
989  if (num_nodes == 0) return true;
990 
991  // Make sure each routes from the depot go back to it, and count such arcs.
992  int count = 0;
993  for (int start : depot_nexts) {
994  ++count;
995  while (start != 0) {
996  if (tail_to_head[start] == -1) return false;
997  start = tail_to_head[start];
998  ++count;
999  }
1000  }
1001 
1002  if (count != num_used_arcs) {
1003  VLOG(1) << "count: " << count << " != num_used_arcs:" << num_used_arcs;
1004  return false;
1005  }
1006 
1007  // Each routes cover as many node as there is arcs, but this way we count
1008  // multiple times the depot. So the number of nodes covered are:
1009  // count - depot_nexts.size() + 1.
1010  // And this number + the self arcs should be num_nodes.
1011  if (count - depot_nexts.size() + 1 + num_self_arcs != num_nodes) {
1012  VLOG(1) << "Not all nodes are covered!";
1013  return false;
1014  }
1015 
1016  return true;
1017  }
1018 
1019  bool InverseConstraintIsFeasible(const ConstraintProto& ct) {
1020  const int num_variables = ct.inverse().f_direct_size();
1021  if (num_variables != ct.inverse().f_inverse_size()) return false;
1022  // Check that f_inverse(f_direct(i)) == i; this is sufficient.
1023  for (int i = 0; i < num_variables; i++) {
1024  const int fi = Value(ct.inverse().f_direct(i));
1025  if (fi < 0 || num_variables <= fi) return false;
1026  if (i != Value(ct.inverse().f_inverse(fi))) return false;
1027  }
1028  return true;
1029  }
1030 
1031  bool ReservoirConstraintIsFeasible(const ConstraintProto& ct) {
1032  const int num_variables = ct.reservoir().times_size();
1033  const int64_t min_level = ct.reservoir().min_level();
1034  const int64_t max_level = ct.reservoir().max_level();
1035  std::map<int64_t, int64_t> deltas;
1036  deltas[0] = 0;
1037  const bool has_active_variables = ct.reservoir().actives_size() > 0;
1038  for (int i = 0; i < num_variables; i++) {
1039  const int64_t time = Value(ct.reservoir().times(i));
1040  if (time < 0) {
1041  VLOG(1) << "reservoir times(" << i << ") is negative.";
1042  return false;
1043  }
1044  if (!has_active_variables || Value(ct.reservoir().actives(i)) == 1) {
1045  deltas[time] += ct.reservoir().demands(i);
1046  }
1047  }
1048  int64_t current_level = 0;
1049  for (const auto& delta : deltas) {
1050  current_level += delta.second;
1051  if (current_level < min_level || current_level > max_level) {
1052  VLOG(1) << "Reservoir level " << current_level
1053  << " is out of bounds at time" << delta.first;
1054  return false;
1055  }
1056  }
1057  return true;
1058  }
1059 
1060  private:
1061  std::vector<int64_t> variable_values_;
1062 };
1063 
1064 } // namespace
1065 
1066 bool SolutionIsFeasible(const CpModelProto& model,
1067  const std::vector<int64_t>& variable_values,
1068  const CpModelProto* mapping_proto,
1069  const std::vector<int>* postsolve_mapping) {
1070  if (variable_values.size() != model.variables_size()) {
1071  VLOG(1) << "Wrong number of variables in the solution vector";
1072  return false;
1073  }
1074 
1075  // Check that all values fall in the variable domains.
1076  for (int i = 0; i < model.variables_size(); ++i) {
1077  if (!DomainInProtoContains(model.variables(i), variable_values[i])) {
1078  VLOG(1) << "Variable #" << i << " has value " << variable_values[i]
1079  << " which do not fall in its domain: "
1080  << ProtobufShortDebugString(model.variables(i));
1081  return false;
1082  }
1083  }
1084 
1085  CHECK_EQ(variable_values.size(), model.variables_size());
1086  ConstraintChecker checker(variable_values);
1087 
1088  for (int c = 0; c < model.constraints_size(); ++c) {
1089  const ConstraintProto& ct = model.constraints(c);
1090 
1091  if (!checker.ConstraintIsEnforced(ct)) continue;
1092 
1093  bool is_feasible = true;
1094  const ConstraintProto::ConstraintCase type = ct.constraint_case();
1095  switch (type) {
1096  case ConstraintProto::ConstraintCase::kBoolOr:
1097  is_feasible = checker.BoolOrConstraintIsFeasible(ct);
1098  break;
1099  case ConstraintProto::ConstraintCase::kBoolAnd:
1100  is_feasible = checker.BoolAndConstraintIsFeasible(ct);
1101  break;
1102  case ConstraintProto::ConstraintCase::kAtMostOne:
1103  is_feasible = checker.AtMostOneConstraintIsFeasible(ct);
1104  break;
1105  case ConstraintProto::ConstraintCase::kExactlyOne:
1106  is_feasible = checker.ExactlyOneConstraintIsFeasible(ct);
1107  break;
1108  case ConstraintProto::ConstraintCase::kBoolXor:
1109  is_feasible = checker.BoolXorConstraintIsFeasible(ct);
1110  break;
1111  case ConstraintProto::ConstraintCase::kLinear:
1112  is_feasible = checker.LinearConstraintIsFeasible(ct);
1113  break;
1114  case ConstraintProto::ConstraintCase::kIntProd:
1115  is_feasible = checker.IntProdConstraintIsFeasible(ct);
1116  break;
1117  case ConstraintProto::ConstraintCase::kIntDiv:
1118  is_feasible = checker.IntDivConstraintIsFeasible(ct);
1119  break;
1120  case ConstraintProto::ConstraintCase::kIntMod:
1121  is_feasible = checker.IntModConstraintIsFeasible(ct);
1122  break;
1123  case ConstraintProto::ConstraintCase::kIntMin:
1124  is_feasible = checker.IntMinConstraintIsFeasible(ct);
1125  break;
1126  case ConstraintProto::ConstraintCase::kLinMin:
1127  is_feasible = checker.LinMinConstraintIsFeasible(ct);
1128  break;
1129  case ConstraintProto::ConstraintCase::kIntMax:
1130  is_feasible = checker.IntMaxConstraintIsFeasible(ct);
1131  break;
1132  case ConstraintProto::ConstraintCase::kLinMax:
1133  is_feasible = checker.LinMaxConstraintIsFeasible(ct);
1134  break;
1135  case ConstraintProto::ConstraintCase::kAllDiff:
1136  is_feasible = checker.AllDiffConstraintIsFeasible(ct);
1137  break;
1138  case ConstraintProto::ConstraintCase::kInterval:
1139  is_feasible = checker.IntervalConstraintIsFeasible(ct);
1140  break;
1141  case ConstraintProto::ConstraintCase::kNoOverlap:
1142  is_feasible = checker.NoOverlapConstraintIsFeasible(model, ct);
1143  break;
1144  case ConstraintProto::ConstraintCase::kNoOverlap2D:
1145  is_feasible = checker.NoOverlap2DConstraintIsFeasible(model, ct);
1146  break;
1147  case ConstraintProto::ConstraintCase::kCumulative:
1148  is_feasible = checker.CumulativeConstraintIsFeasible(model, ct);
1149  break;
1150  case ConstraintProto::ConstraintCase::kElement:
1151  is_feasible = checker.ElementConstraintIsFeasible(ct);
1152  break;
1153  case ConstraintProto::ConstraintCase::kTable:
1154  is_feasible = checker.TableConstraintIsFeasible(ct);
1155  break;
1156  case ConstraintProto::ConstraintCase::kAutomaton:
1157  is_feasible = checker.AutomatonConstraintIsFeasible(ct);
1158  break;
1159  case ConstraintProto::ConstraintCase::kCircuit:
1160  is_feasible = checker.CircuitConstraintIsFeasible(ct);
1161  break;
1162  case ConstraintProto::ConstraintCase::kRoutes:
1163  is_feasible = checker.RoutesConstraintIsFeasible(ct);
1164  break;
1165  case ConstraintProto::ConstraintCase::kInverse:
1166  is_feasible = checker.InverseConstraintIsFeasible(ct);
1167  break;
1168  case ConstraintProto::ConstraintCase::kReservoir:
1169  is_feasible = checker.ReservoirConstraintIsFeasible(ct);
1170  break;
1171  case ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET:
1172  // Empty constraint is always feasible.
1173  break;
1174  default:
1175  LOG(FATAL) << "Unuspported constraint: " << ConstraintCaseName(type);
1176  }
1177 
1178  // Display a message to help debugging.
1179  if (!is_feasible) {
1180  VLOG(1) << "Failing constraint #" << c << " : "
1181  << ProtobufShortDebugString(model.constraints(c));
1182  if (mapping_proto != nullptr && postsolve_mapping != nullptr) {
1183  std::vector<int> reverse_map(mapping_proto->variables().size(), -1);
1184  for (int var = 0; var < postsolve_mapping->size(); ++var) {
1185  reverse_map[(*postsolve_mapping)[var]] = var;
1186  }
1187  for (const int var : UsedVariables(model.constraints(c))) {
1188  VLOG(1) << "var: " << var << " mapped_to: " << reverse_map[var]
1189  << " value: " << variable_values[var] << " initial_domain: "
1190  << ReadDomainFromProto(model.variables(var))
1191  << " postsolved_domain: "
1192  << ReadDomainFromProto(mapping_proto->variables(var));
1193  }
1194  } else {
1195  for (const int var : UsedVariables(model.constraints(c))) {
1196  VLOG(1) << "var: " << var << " value: " << variable_values[var];
1197  }
1198  }
1199  return false;
1200  }
1201  }
1202  return true;
1203 }
1204 
1205 } // namespace sat
1206 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:705
#define LOG(severity)
Definition: base/logging.h:423
#define VLOG(verboselevel)
Definition: base/logging.h:986
We call domain any subset of Int64 = [kint64min, kint64max].
int64_t Size() const
Returns the number of elements in the domain.
#define RETURN_IF_NOT_EMPTY(statement)
CpModelProto proto
const Constraint * ct
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
const int FATAL
Definition: log_severity.h:32
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:200
std::vector< int > UsedVariables(const ConstraintProto &ct)
bool RefIsPositive(int ref)
std::vector< int > UsedIntervals(const ConstraintProto &ct)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
Definition: integer.h:1492
bool DomainInProtoContains(const ProtoWithDomain &proto, int64_t value)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
IndexReferences GetReferencesUsedByConstraint(const ConstraintProto &ct)
std::string ConstraintCaseName(ConstraintProto::ConstraintCase constraint_case)
bool SolutionIsFeasible(const CpModelProto &model, const std::vector< int64_t > &variable_values, const CpModelProto *mapping_proto, const std::vector< int > *postsolve_mapping)
std::string ValidateCpModel(const CpModelProto &model)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
std::string ProtobufShortDebugString(const P &message)
int64_t CapProd(int64_t x, int64_t y)
std::string ProtobufDebugString(const P &message)
bool IntervalsAreSortedAndNonAdjacent(absl::Span< const ClosedInterval > intervals)
Returns true iff we have:
int index
Definition: pack.cc:509
int64_t demand
Definition: resource.cc:125
int64_t time
Definition: resource.cc:1691
int64_t delta
Definition: resource.cc:1692
IntervalVar * interval
Definition: resource.cc:100
int64_t capacity
int64_t tail
int64_t head
int nodes