25 #include "absl/container/btree_map.h"
26 #include "absl/container/flat_hash_map.h"
27 #include "absl/container/flat_hash_set.h"
28 #include "absl/meta/type_traits.h"
29 #include "absl/strings/str_cat.h"
32 #include "ortools/sat/cp_model.pb.h"
34 #include "ortools/sat/sat_parameters.pb.h"
47 #define RETURN_IF_NOT_EMPTY(statement) \
49 const std::string error_message = statement; \
50 if (!error_message.empty()) return error_message; \
53 template <
typename ProtoWithDomain>
54 bool DomainInProtoIsValid(
const ProtoWithDomain&
proto) {
55 if (
proto.domain().size() % 2)
return false;
56 std::vector<ClosedInterval> domain;
57 for (
int i = 0; i <
proto.domain_size(); i += 2) {
58 if (
proto.domain(i) >
proto.domain(i + 1))
return false;
59 domain.push_back({
proto.domain(i),
proto.domain(i + 1)});
64 bool VariableReferenceIsValid(
const CpModelProto&
model,
int reference) {
66 if (reference >=
model.variables_size())
return false;
67 return reference >= -
static_cast<int>(
model.variables_size());
74 bool VariableIndexIsValid(
const CpModelProto&
model,
int var) {
78 bool LiteralReferenceIsValid(
const CpModelProto&
model,
int reference) {
79 if (!VariableReferenceIsValid(
model, reference))
return false;
81 const int64_t min_domain = var_proto.domain(0);
82 const int64_t max_domain = var_proto.domain(var_proto.domain_size() - 1);
83 return min_domain >= 0 && max_domain <= 1;
86 std::string ValidateIntegerVariable(
const CpModelProto&
model,
int v) {
87 const IntegerVariableProto&
proto =
model.variables(v);
88 if (
proto.domain_size() == 0) {
89 return absl::StrCat(
"var #", v,
92 if (
proto.domain_size() % 2 != 0) {
93 return absl::StrCat(
"var #", v,
" has an odd domain() size: ",
96 if (!DomainInProtoIsValid(
proto)) {
97 return absl::StrCat(
"var #", v,
" has and invalid domain() format: ",
104 const int64_t lb =
proto.domain(0);
105 const int64_t ub =
proto.domain(
proto.domain_size() - 1);
109 "var #", v,
" domain do not fall in [kint64min + 2, kint64max - 1]. ",
118 " has a domain that is too large, i.e. |UB - LB| overflow an int64_t: ",
125 std::string ValidateVariablesUsedInConstraint(
const CpModelProto&
model,
127 const ConstraintProto&
ct =
model.constraints(c);
129 for (
const int v : references.variables) {
130 if (!VariableReferenceIsValid(
model, v)) {
131 return absl::StrCat(
"Out of bound integer variable ", v,
132 " in constraint #", c,
" : ",
136 for (
const int lit :
ct.enforcement_literal()) {
137 if (!LiteralReferenceIsValid(
model, lit)) {
138 return absl::StrCat(
"Invalid enforcement literal ", lit,
139 " in constraint #", c,
" : ",
143 for (
const int lit : references.literals) {
144 if (!LiteralReferenceIsValid(
model, lit)) {
145 return absl::StrCat(
"Invalid literal ", lit,
" in constraint #", c,
" : ",
152 std::string ValidateIntervalsUsedInConstraint(
bool after_presolve,
153 const CpModelProto&
model,
155 const ConstraintProto&
ct =
model.constraints(c);
157 if (i < 0 || i >=
model.constraints_size()) {
158 return absl::StrCat(
"Out of bound interval ", i,
" in constraint #", c,
161 if (after_presolve && i >= c) {
162 return absl::StrCat(
"Interval ", i,
" in constraint #", c,
163 " must appear before in the list of constraints :",
166 if (
model.constraints(i).constraint_case() !=
167 ConstraintProto::ConstraintCase::kInterval) {
170 " does not refer to an interval constraint. Problematic constraint #",
177 int64_t MinOfRef(
const CpModelProto&
model,
int ref) {
180 return var_proto.domain(0);
182 return -var_proto.domain(var_proto.domain_size() - 1);
186 int64_t MaxOfRef(
const CpModelProto&
model,
int ref) {
189 return var_proto.domain(var_proto.domain_size() - 1);
191 return -var_proto.domain(0);
195 template <
class LinearExpressionProto>
196 int64_t MinOfExpression(
const CpModelProto&
model,
197 const LinearExpressionProto&
proto) {
198 int64_t sum_min =
proto.offset();
199 for (
int i = 0; i <
proto.vars_size(); ++i) {
200 const int ref =
proto.vars(i);
201 const int64_t coeff =
proto.coeffs(i);
210 template <
class LinearExpressionProto>
211 int64_t MaxOfExpression(
const CpModelProto&
model,
212 const LinearExpressionProto&
proto) {
213 int64_t sum_max =
proto.offset();
214 for (
int i = 0; i <
proto.vars_size(); ++i) {
215 const int ref =
proto.vars(i);
216 const int64_t coeff =
proto.coeffs(i);
226 DCHECK_EQ(ConstraintProto::ConstraintCase::kInterval,
228 const IntervalConstraintProto&
proto =
234 DCHECK_EQ(ConstraintProto::ConstraintCase::kInterval,
236 const IntervalConstraintProto&
proto =
241 Domain DomainOfRef(
const CpModelProto&
model,
int ref) {
247 const LinearExpressionProto& expr) {
248 if (expr.coeffs_size() != expr.vars_size()) {
249 return absl::StrCat(
"coeffs_size() != vars_size() in linear expression: ",
254 return absl::StrCat(
"Possible overflow in linear expression: ",
260 std::string ValidateAffineExpression(
const CpModelProto&
model,
261 const LinearExpressionProto& expr) {
262 if (expr.vars_size() > 1) {
263 return absl::StrCat(
"expression must be affine: ",
269 std::string ValidateConstantAffineExpression(
270 const CpModelProto&
model,
const LinearExpressionProto& expr) {
271 if (!expr.vars().empty()) {
272 return absl::StrCat(
"expression must be constant: ",
278 std::string ValidateLinearConstraint(
const CpModelProto&
model,
279 const ConstraintProto&
ct) {
280 if (!DomainInProtoIsValid(
ct.linear())) {
281 return absl::StrCat(
"Invalid domain in constraint : ",
284 if (
ct.linear().coeffs_size() !=
ct.linear().vars_size()) {
285 return absl::StrCat(
"coeffs_size() != vars_size() in constraint: ",
288 const LinearConstraintProto& arg =
ct.linear();
290 return "Possible integer overflow in constraint: " +
296 std::string ValidateIntModConstraint(
const CpModelProto&
model,
297 const ConstraintProto&
ct) {
298 if (
ct.int_mod().exprs().size() != 2) {
299 return absl::StrCat(
"An int_mod constraint should have exactly 2 terms: ",
302 if (!
ct.int_mod().has_target()) {
303 return absl::StrCat(
"An int_mod constraint should have a target: ",
311 const LinearExpressionProto mod_expr =
ct.int_mod().exprs(1);
312 if (MinOfExpression(
model, mod_expr) <= 0) {
314 "An int_mod must have a strictly positive modulo argument: ",
321 std::string ValidateIntProdConstraint(
const CpModelProto&
model,
322 const ConstraintProto&
ct) {
323 if (
ct.int_prod().exprs().size() != 2) {
324 return absl::StrCat(
"An int_prod constraint should have exactly 2 terms: ",
327 if (!
ct.int_prod().has_target()) {
328 return absl::StrCat(
"An int_prod constraint should have a target: ",
337 const LinearExpressionProto& expr0 =
ct.int_prod().exprs(0);
338 const LinearExpressionProto& expr1 =
ct.int_prod().exprs(1);
339 const Domain product_domain =
340 Domain({MinOfExpression(
model, expr0), MaxOfExpression(
model, expr0)})
341 .ContinuousMultiplicationBy(Domain(
342 {MinOfExpression(
model, expr1), MaxOfExpression(
model, expr1)}));
344 product_domain.Min() < 0) ||
346 product_domain.Max() > 0)) {
347 return absl::StrCat(
"Potential integer overflow in constraint: ",
353 std::string ValidateIntDivConstraint(
const CpModelProto&
model,
354 const ConstraintProto&
ct) {
355 if (
ct.int_div().exprs().size() != 2) {
356 return absl::StrCat(
"An int_div constraint should have exactly 2 terms: ",
359 if (!
ct.int_div().has_target()) {
360 return absl::StrCat(
"An int_div constraint should have a target: ",
368 const LinearExpressionProto& divisor_proto =
ct.int_div().exprs(1);
369 if (MinOfExpression(
model, divisor_proto) <= 0 &&
370 MaxOfExpression(
model, divisor_proto) >= 0) {
371 return absl::StrCat(
"The divisor cannot span across zero in constraint: ",
378 std::string ValidateElementConstraint(
const CpModelProto&
model,
379 const ConstraintProto&
ct) {
380 const ElementConstraintProto& element =
ct.element();
384 LinearExpressionProto overflow_detection;
385 overflow_detection.add_vars(element.target());
386 overflow_detection.add_coeffs(1);
387 overflow_detection.add_vars( 0);
388 overflow_detection.add_coeffs(-1);
389 for (
const int ref : element.vars()) {
390 overflow_detection.set_vars(1, ref);
392 overflow_detection.coeffs())) {
394 "Domain of the variables involved in element constraint may cause "
402 std::string ValidateTableConstraint(
const CpModelProto&
model,
403 const ConstraintProto&
ct) {
404 const TableConstraintProto& arg =
ct.table();
405 if (arg.vars().empty())
return "";
406 if (arg.values().size() % arg.vars().size() != 0) {
408 "The flat encoding of a table constraint must be a multiple of the "
409 "number of variable: ",
415 std::string ValidateAutomatonConstraint(
const CpModelProto&
model,
416 const ConstraintProto&
ct) {
417 const int num_transistions =
ct.automaton().transition_tail().size();
418 if (num_transistions !=
ct.automaton().transition_head().size() ||
419 num_transistions !=
ct.automaton().transition_label().size()) {
421 "The transitions repeated fields must have the same size: ",
424 absl::flat_hash_map<std::pair<int64_t, int64_t>, int64_t> tail_label_to_head;
425 for (
int i = 0; i < num_transistions; ++i) {
426 const int64_t
tail =
ct.automaton().transition_tail(i);
427 const int64_t
head =
ct.automaton().transition_head(i);
428 const int64_t label =
ct.automaton().transition_label(i);
431 return absl::StrCat(
"labels in the automaton constraint are too big: ",
434 const auto [it, inserted] =
435 tail_label_to_head.insert({{
tail, label},
head});
437 if (it->second ==
head) {
438 return absl::StrCat(
"automaton: duplicate transition ",
tail,
" --(",
439 label,
")--> ",
head);
441 return absl::StrCat(
"automaton: incompatible transitions ",
tail,
442 " --(", label,
")--> ",
head,
" and ",
tail,
" --(",
443 label,
")--> ", it->second);
450 template <
typename GraphProto>
451 std::string ValidateGraphInput(
bool is_route,
const CpModelProto&
model,
452 const GraphProto& graph) {
453 const int size = graph.tails().size();
454 if (graph.heads().size() != size || graph.literals().size() != size) {
455 return absl::StrCat(
"Wrong field sizes in graph: ",
460 absl::flat_hash_set<int> self_loops;
461 for (
int i = 0; i < size; ++i) {
462 if (graph.heads(i) != graph.tails(i))
continue;
463 if (!self_loops.insert(graph.heads(i)).second) {
465 "Circuit/Route constraint contains multiple self-loop involving "
469 if (is_route && graph.tails(i) == 0) {
471 "A route constraint cannot have a self-loop on the depot (node 0)");
478 std::string ValidateRoutesConstraint(
const CpModelProto&
model,
479 const ConstraintProto&
ct) {
481 absl::flat_hash_set<int>
nodes;
482 for (
const int node :
ct.routes().tails()) {
484 return "All node in a route constraint must be in [0, num_nodes)";
487 max_node =
std::max(max_node, node);
489 for (
const int node :
ct.routes().heads()) {
491 return "All node in a route constraint must be in [0, num_nodes)";
494 max_node =
std::max(max_node, node);
496 if (!
nodes.empty() && max_node !=
nodes.size() - 1) {
498 "All nodes in a route constraint must have incident arcs");
501 return ValidateGraphInput(
true,
model,
ct.routes());
504 std::string ValidateDomainIsPositive(
const CpModelProto&
model,
int ref,
505 const std::string& ref_name) {
507 const IntegerVariableProto& var_proto =
model.variables(
NegatedRef(ref));
508 if (var_proto.domain(var_proto.domain_size() - 1) > 0) {
509 return absl::StrCat(
"Negative value in ", ref_name,
510 " domain: negation of ",
514 const IntegerVariableProto& var_proto =
model.variables(ref);
515 if (var_proto.domain(0) < 0) {
516 return absl::StrCat(
"Negative value in ", ref_name,
523 void AppendToOverflowValidator(
const LinearExpressionProto&
input,
524 LinearExpressionProto* output) {
525 output->mutable_vars()->Add(
input.vars().begin(),
input.vars().end());
526 output->mutable_coeffs()->Add(
input.coeffs().begin(),
input.coeffs().end());
531 CapAdd(std::abs(output->offset()), std::abs(
input.offset())));
534 std::string ValidateIntervalConstraint(
const CpModelProto&
model,
535 const ConstraintProto&
ct) {
536 if (
ct.enforcement_literal().size() > 1) {
538 "Interval with more than one enforcement literals are currently not "
542 const IntervalConstraintProto& arg =
ct.interval();
544 if (!arg.has_start()) {
545 return absl::StrCat(
"Interval must have a start expression: ",
548 if (!arg.has_size()) {
549 return absl::StrCat(
"Interval must have a size expression: ",
552 if (!arg.has_end()) {
553 return absl::StrCat(
"Interval must have a end expression: ",
557 LinearExpressionProto for_overflow_validation;
558 if (arg.start().vars_size() > 1) {
559 return "Interval with a start expression containing more than one "
560 "variable are currently not supported.";
563 AppendToOverflowValidator(arg.start(), &for_overflow_validation);
564 if (arg.size().vars_size() > 1) {
565 return "Interval with a size expression containing more than one "
566 "variable are currently not supported.";
569 if (
ct.enforcement_literal().empty() &&
570 MinOfExpression(
model, arg.size()) < 0) {
572 "The size of an performed interval must be >= 0 in constraint: ",
575 AppendToOverflowValidator(arg.size(), &for_overflow_validation);
576 if (arg.end().vars_size() > 1) {
577 return "Interval with a end expression containing more than one "
578 "variable are currently not supported.";
581 AppendToOverflowValidator(arg.end(), &for_overflow_validation);
584 for_overflow_validation.coeffs(),
585 for_overflow_validation.offset())) {
586 return absl::StrCat(
"Possible overflow in interval: ",
593 std::string ValidateCumulativeConstraint(
const CpModelProto&
model,
594 const ConstraintProto&
ct) {
595 if (
ct.cumulative().intervals_size() !=
ct.cumulative().demands_size()) {
596 return absl::StrCat(
"intervals_size() != demands_size() in constraint: ",
602 for (
const LinearExpressionProto&
demand :
ct.cumulative().demands()) {
606 for (
const LinearExpressionProto& demand_expr :
ct.cumulative().demands()) {
607 if (MinOfExpression(
model, demand_expr) < 0) {
612 if (demand_expr.vars_size() > 1) {
614 " must be affine or constant in constraint: ",
618 if (
ct.cumulative().capacity().vars_size() > 1) {
624 int64_t sum_max_demands = 0;
625 for (
const LinearExpressionProto& demand_expr :
ct.cumulative().demands()) {
626 const int64_t demand_max = MaxOfExpression(
model, demand_expr);
627 DCHECK_GE(demand_max, 0);
628 sum_max_demands =
CapAdd(sum_max_demands, demand_max);
630 return "The sum of max demands do not fit on an int64_t in constraint: " +
638 std::string ValidateNoOverlap2DConstraint(
const CpModelProto&
model,
639 const ConstraintProto&
ct) {
640 const int size_x =
ct.no_overlap_2d().x_intervals().size();
641 const int size_y =
ct.no_overlap_2d().y_intervals().size();
642 if (size_x != size_y) {
643 return absl::StrCat(
"The two lists of intervals must have the same size: ",
648 int64_t sum_max_areas = 0;
649 for (
int i = 0; i <
ct.no_overlap_2d().x_intervals().size(); ++i) {
650 const int64_t max_size_x =
651 IntervalSizeMax(
model,
ct.no_overlap_2d().x_intervals(i));
652 const int64_t max_size_y =
653 IntervalSizeMax(
model,
ct.no_overlap_2d().y_intervals(i));
654 sum_max_areas =
CapAdd(sum_max_areas,
CapProd(max_size_x, max_size_y));
656 return "Integer overflow when summing all areas in "
664 std::string ValidateReservoirConstraint(
const CpModelProto&
model,
665 const ConstraintProto&
ct) {
666 if (
ct.enforcement_literal_size() > 0) {
667 return "Reservoir does not support enforcement literals.";
669 if (
ct.reservoir().time_exprs().size() !=
670 ct.reservoir().level_changes().size()) {
672 "time_exprs and level_changes fields must be of the same size: ",
675 for (
const LinearExpressionProto& expr :
ct.reservoir().time_exprs()) {
678 for (
const LinearExpressionProto& expr :
ct.reservoir().level_changes()) {
681 if (
ct.reservoir().min_level() > 0) {
683 "The min level of a reservoir must be <= 0. Please use fixed events to "
684 "setup initial state: ",
687 if (
ct.reservoir().max_level() < 0) {
689 "The max level of a reservoir must be >= 0. Please use fixed events to "
690 "setup initial state: ",
695 for (
const LinearExpressionProto&
demand :
ct.reservoir().level_changes()) {
697 const int64_t demand_min = MinOfExpression(
model,
demand);
698 const int64_t demand_max = MaxOfExpression(
model,
demand);
701 return "Possible integer overflow in constraint: " +
705 if (
ct.reservoir().active_literals_size() > 0 &&
706 ct.reservoir().active_literals_size() !=
707 ct.reservoir().time_exprs_size()) {
708 return "Wrong array length of active_literals variables";
710 if (
ct.reservoir().level_changes_size() > 0 &&
711 ct.reservoir().level_changes_size() !=
ct.reservoir().time_exprs_size()) {
712 return "Wrong array length of level_changes variables";
717 std::string ValidateObjective(
const CpModelProto&
model,
718 const CpObjectiveProto& obj) {
719 if (!DomainInProtoIsValid(obj)) {
720 return absl::StrCat(
"The objective has and invalid domain() format: ",
723 if (obj.vars().size() != obj.coeffs().size()) {
724 return absl::StrCat(
"vars and coeffs size do not match in objective: ",
727 for (
const int v : obj.vars()) {
728 if (!VariableReferenceIsValid(
model, v)) {
729 return absl::StrCat(
"Out of bound integer variable ", v,
734 return "Possible integer overflow in objective: " +
740 std::string ValidateFloatingPointObjective(
double max_valid_magnitude,
741 const CpModelProto&
model,
742 const FloatObjectiveProto& obj) {
743 if (obj.vars().size() != obj.coeffs().size()) {
744 return absl::StrCat(
"vars and coeffs size do not match in objective: ",
747 for (
const int v : obj.vars()) {
748 if (!VariableIndexIsValid(
model, v)) {
749 return absl::StrCat(
"Out of bound integer variable ", v,
753 for (
const double coeff : obj.coeffs()) {
754 if (!std::isfinite(coeff)) {
755 return absl::StrCat(
"Coefficients must be finite in objective: ",
758 if (std::abs(coeff) > max_valid_magnitude) {
760 "Coefficients larger than params.mip_max_valid_magnitude() [value = ",
765 if (!std::isfinite(obj.offset())) {
766 return absl::StrCat(
"Offset must be finite in objective: ",
772 std::string ValidateSearchStrategies(
const CpModelProto&
model) {
773 for (
const DecisionStrategyProto& strategy :
model.search_strategy()) {
774 const int vss = strategy.variable_selection_strategy();
775 if (vss != DecisionStrategyProto::CHOOSE_FIRST &&
776 vss != DecisionStrategyProto::CHOOSE_LOWEST_MIN &&
777 vss != DecisionStrategyProto::CHOOSE_HIGHEST_MAX &&
778 vss != DecisionStrategyProto::CHOOSE_MIN_DOMAIN_SIZE &&
779 vss != DecisionStrategyProto::CHOOSE_MAX_DOMAIN_SIZE) {
781 "Unknown or unsupported variable_selection_strategy: ", vss);
783 const int drs = strategy.domain_reduction_strategy();
784 if (drs != DecisionStrategyProto::SELECT_MIN_VALUE &&
785 drs != DecisionStrategyProto::SELECT_MAX_VALUE &&
786 drs != DecisionStrategyProto::SELECT_LOWER_HALF &&
787 drs != DecisionStrategyProto::SELECT_UPPER_HALF &&
788 drs != DecisionStrategyProto::SELECT_MEDIAN_VALUE) {
789 return absl::StrCat(
"Unknown or unsupported domain_reduction_strategy: ",
792 for (
const int ref : strategy.variables()) {
793 if (!VariableReferenceIsValid(
model, ref)) {
794 return absl::StrCat(
"Invalid variable reference in strategy: ",
797 if (drs == DecisionStrategyProto::SELECT_MEDIAN_VALUE &&
800 return absl::StrCat(
"Variable #",
PositiveRef(ref),
801 " has a domain too large to be used in a"
802 " SELECT_MEDIAN_VALUE value selection strategy");
805 int previous_index = -1;
806 for (
const auto& transformation : strategy.transformations()) {
807 if (transformation.positive_coeff() <= 0) {
808 return absl::StrCat(
"Affine transformation coeff should be positive: ",
811 if (transformation.index() <= previous_index ||
812 transformation.index() >= strategy.variables_size()) {
814 "Invalid indices (must be sorted and valid) in transformation: ",
817 previous_index = transformation.index();
823 std::string ValidateSolutionHint(
const CpModelProto&
model) {
824 if (!
model.has_solution_hint())
return "";
825 const auto& hint =
model.solution_hint();
826 if (hint.vars().size() != hint.values().size()) {
827 return "Invalid solution hint: vars and values do not have the same size.";
829 for (
const int ref : hint.vars()) {
830 if (!VariableReferenceIsValid(
model, ref)) {
831 return absl::StrCat(
"Invalid variable reference in solution hint: ", ref);
836 absl::flat_hash_set<int> indices;
837 for (
const int var : hint.vars()) {
839 if (!insert.second) {
841 "The solution hint contains duplicate variables like the variable "
848 for (
const int64_t
value : hint.values()) {
851 return "The solution hint cannot contains the INT_MIN or INT_MAX values.";
861 absl::Span<const int> vars,
862 absl::Span<const int64_t> coeffs, int64_t offset) {
864 int64_t sum_min = -std::abs(offset);
865 int64_t sum_max = +std::abs(offset);
866 for (
int i = 0; i < vars.size(); ++i) {
867 const int ref = vars[i];
869 const int64_t min_domain = var_proto.domain(0);
870 const int64_t max_domain = var_proto.domain(var_proto.domain_size() - 1);
872 const int64_t coeff =
RefIsPositive(ref) ? coeffs[i] : -coeffs[i];
873 const int64_t prod1 =
CapProd(min_domain, coeff);
874 const int64_t prod2 =
CapProd(max_domain, coeff);
881 for (
const int64_t v : {prod1, prod2, sum_min, sum_max}) {
897 for (
int v = 0; v <
model.variables_size(); ++v) {
903 std::vector<int> constraints_using_intervals;
905 for (
int c = 0; c <
model.constraints_size(); ++c) {
910 bool support_enforcement =
false;
913 const ConstraintProto&
ct =
model.constraints(c);
914 switch (
ct.constraint_case()) {
915 case ConstraintProto::ConstraintCase::kBoolOr:
916 support_enforcement =
true;
918 case ConstraintProto::ConstraintCase::kBoolAnd:
919 support_enforcement =
true;
921 case ConstraintProto::ConstraintCase::kLinear:
922 support_enforcement =
true;
925 case ConstraintProto::ConstraintCase::kLinMax: {
928 for (
const LinearExpressionProto& expr :
ct.lin_max().exprs()) {
933 case ConstraintProto::ConstraintCase::kIntProd:
936 case ConstraintProto::ConstraintCase::kIntDiv:
939 case ConstraintProto::ConstraintCase::kIntMod:
942 case ConstraintProto::ConstraintCase::kInverse:
943 if (
ct.inverse().f_direct().size() !=
ct.inverse().f_inverse().size()) {
944 return absl::StrCat(
"Non-matching fields size in inverse: ",
948 case ConstraintProto::ConstraintCase::kAllDiff:
949 for (
const LinearExpressionProto& expr :
ct.all_diff().exprs()) {
953 case ConstraintProto::ConstraintCase::kElement:
956 case ConstraintProto::ConstraintCase::kTable:
959 case ConstraintProto::ConstraintCase::kAutomaton:
962 case ConstraintProto::ConstraintCase::kCircuit:
964 ValidateGraphInput(
false,
model,
ct.circuit()));
966 case ConstraintProto::ConstraintCase::kRoutes:
969 case ConstraintProto::ConstraintCase::kInterval:
971 support_enforcement =
true;
973 case ConstraintProto::ConstraintCase::kCumulative:
974 constraints_using_intervals.push_back(c);
976 case ConstraintProto::ConstraintCase::kNoOverlap:
977 constraints_using_intervals.push_back(c);
979 case ConstraintProto::ConstraintCase::kNoOverlap2D:
980 constraints_using_intervals.push_back(c);
982 case ConstraintProto::ConstraintCase::kReservoir:
985 case ConstraintProto::ConstraintCase::kDummyConstraint:
986 return "The dummy constraint should never appear in a model.";
994 if (!support_enforcement && !
ct.enforcement_literal().empty()) {
995 for (
const int ref :
ct.enforcement_literal()) {
998 if (domain.
Size() != 1) {
1000 "Enforcement literal not supported in constraint: ",
1008 for (
const int c : constraints_using_intervals) {
1010 ValidateIntervalsUsedInConstraint(after_presolve,
model, c));
1012 const ConstraintProto&
ct =
model.constraints(c);
1013 switch (
ct.constraint_case()) {
1014 case ConstraintProto::ConstraintCase::kCumulative:
1017 case ConstraintProto::ConstraintCase::kNoOverlap:
1019 case ConstraintProto::ConstraintCase::kNoOverlap2D:
1023 LOG(DFATAL) <<
"Shouldn't be here";
1027 if (
model.has_objective() &&
model.has_floating_point_objective()) {
1028 return "A model cannot have both an objective and a floating point "
1031 if (
model.has_objective()) {
1034 if (
model.objective().integer_scaling_factor() != 0 ||
1035 model.objective().integer_before_offset() != 0 ||
1036 model.objective().integer_after_offset() != 0) {
1038 if (
model.objective().domain().empty()) {
1039 return absl::StrCat(
1040 "Objective integer scaling or offset is set without an objective "
1046 bool overflow =
false;
1047 for (
const int64_t v :
model.objective().domain()) {
1048 int64_t t =
CapAdd(v,
model.objective().integer_before_offset());
1053 t =
CapProd(t,
model.objective().integer_scaling_factor());
1058 t =
CapAdd(t,
model.objective().integer_after_offset());
1065 return absl::StrCat(
1066 "Internal fields related to the postsolve of the integer objective "
1067 "are causing a potential integer overflow: ",
1074 for (
const int ref :
model.assumptions()) {
1075 if (!LiteralReferenceIsValid(
model, ref)) {
1076 return absl::StrCat(
"Invalid literal reference ", ref,
1077 " in the 'assumptions' field.");
1084 const CpModelProto&
model) {
1086 if (
model.has_floating_point_objective()) {
1088 ValidateFloatingPointObjective(params.mip_max_valid_magnitude(),
model,
1089 model.floating_point_objective()));
1094 #undef RETURN_IF_NOT_EMPTY
1102 class ConstraintChecker {
1104 explicit ConstraintChecker(absl::Span<const int64_t> variable_values)
1105 : variable_values_(variable_values.begin(), variable_values.
end()) {}
1107 bool LiteralIsTrue(
int l)
const {
1108 if (l >= 0)
return variable_values_[l] != 0;
1109 return variable_values_[-l - 1] == 0;
1112 bool LiteralIsFalse(
int l)
const {
return !LiteralIsTrue(l); }
1115 if (
var >= 0)
return variable_values_[
var];
1116 return -variable_values_[-
var - 1];
1119 bool ConstraintIsEnforced(
const ConstraintProto&
ct) {
1120 for (
const int lit :
ct.enforcement_literal()) {
1121 if (LiteralIsFalse(lit))
return false;
1126 bool BoolOrConstraintIsFeasible(
const ConstraintProto&
ct) {
1127 for (
const int lit :
ct.bool_or().literals()) {
1128 if (LiteralIsTrue(lit))
return true;
1133 bool BoolAndConstraintIsFeasible(
const ConstraintProto&
ct) {
1134 for (
const int lit :
ct.bool_and().literals()) {
1135 if (LiteralIsFalse(lit))
return false;
1140 bool AtMostOneConstraintIsFeasible(
const ConstraintProto&
ct) {
1141 int num_true_literals = 0;
1142 for (
const int lit :
ct.at_most_one().literals()) {
1143 if (LiteralIsTrue(lit)) ++num_true_literals;
1145 return num_true_literals <= 1;
1148 bool ExactlyOneConstraintIsFeasible(
const ConstraintProto&
ct) {
1149 int num_true_literals = 0;
1150 for (
const int lit :
ct.exactly_one().literals()) {
1151 if (LiteralIsTrue(lit)) ++num_true_literals;
1153 return num_true_literals == 1;
1156 bool BoolXorConstraintIsFeasible(
const ConstraintProto&
ct) {
1158 for (
const int lit :
ct.bool_xor().literals()) {
1159 sum ^= LiteralIsTrue(lit) ? 1 : 0;
1164 bool LinearConstraintIsFeasible(
const ConstraintProto&
ct) {
1166 const int num_variables =
ct.linear().coeffs_size();
1167 for (
int i = 0; i < num_variables; ++i) {
1168 sum +=
Value(
ct.linear().vars(i)) *
ct.linear().coeffs(i);
1172 VLOG(1) <<
"Activity: " << sum;
1177 int64_t LinearExpressionValue(
const LinearExpressionProto& expr)
const {
1178 int64_t sum = expr.offset();
1179 const int num_variables = expr.vars_size();
1180 for (
int i = 0; i < num_variables; ++i) {
1181 sum +=
Value(expr.vars(i)) * expr.coeffs(i);
1186 bool LinMaxConstraintIsFeasible(
const ConstraintProto&
ct) {
1187 const int64_t
max = LinearExpressionValue(
ct.lin_max().target());
1189 for (
int i = 0; i <
ct.lin_max().exprs_size(); ++i) {
1190 const int64_t expr_value = LinearExpressionValue(
ct.lin_max().exprs(i));
1191 actual_max =
std::max(actual_max, expr_value);
1193 return max == actual_max;
1196 bool IntProdConstraintIsFeasible(
const ConstraintProto&
ct) {
1197 const int64_t prod = LinearExpressionValue(
ct.int_prod().target());
1198 int64_t actual_prod = 1;
1199 for (
const LinearExpressionProto& expr :
ct.int_prod().exprs()) {
1200 actual_prod =
CapProd(actual_prod, LinearExpressionValue(expr));
1202 return prod == actual_prod;
1205 bool IntDivConstraintIsFeasible(
const ConstraintProto&
ct) {
1206 return LinearExpressionValue(
ct.int_div().target()) ==
1207 LinearExpressionValue(
ct.int_div().exprs(0)) /
1208 LinearExpressionValue(
ct.int_div().exprs(1));
1211 bool IntModConstraintIsFeasible(
const ConstraintProto&
ct) {
1212 return LinearExpressionValue(
ct.int_mod().target()) ==
1213 LinearExpressionValue(
ct.int_mod().exprs(0)) %
1214 LinearExpressionValue(
ct.int_mod().exprs(1));
1217 bool AllDiffConstraintIsFeasible(
const ConstraintProto&
ct) {
1218 absl::flat_hash_set<int64_t> values;
1219 for (
const LinearExpressionProto& expr :
ct.all_diff().exprs()) {
1220 const int64_t
value = LinearExpressionValue(expr);
1221 const auto [it, inserted] = values.insert(
value);
1222 if (!inserted)
return false;
1227 int64_t IntervalStart(
const IntervalConstraintProto&
interval)
const {
1228 return LinearExpressionValue(
interval.start());
1231 int64_t IntervalSize(
const IntervalConstraintProto&
interval)
const {
1232 return LinearExpressionValue(
interval.size());
1235 int64_t IntervalEnd(
const IntervalConstraintProto&
interval)
const {
1236 return LinearExpressionValue(
interval.end());
1239 bool IntervalConstraintIsFeasible(
const ConstraintProto&
ct) {
1240 const int64_t size = IntervalSize(
ct.interval());
1241 if (size < 0)
return false;
1242 return IntervalStart(
ct.interval()) + size == IntervalEnd(
ct.interval());
1245 bool NoOverlapConstraintIsFeasible(
const CpModelProto&
model,
1246 const ConstraintProto&
ct) {
1247 std::vector<std::pair<int64_t, int64_t>> start_durations_pairs;
1248 for (
const int i :
ct.no_overlap().intervals()) {
1249 const ConstraintProto& interval_constraint =
model.constraints(i);
1250 if (ConstraintIsEnforced(interval_constraint)) {
1251 const IntervalConstraintProto&
interval =
1252 interval_constraint.interval();
1253 start_durations_pairs.push_back(
1257 std::sort(start_durations_pairs.begin(), start_durations_pairs.end());
1259 for (
const auto& pair : start_durations_pairs) {
1260 if (pair.first < previous_end)
return false;
1261 previous_end = pair.first + pair.second;
1266 bool IntervalsAreDisjoint(
const IntervalConstraintProto& interval1,
1267 const IntervalConstraintProto& interval2) {
1268 return IntervalEnd(interval1) <= IntervalStart(interval2) ||
1269 IntervalEnd(interval2) <= IntervalStart(interval1);
1272 bool IntervalIsEmpty(
const IntervalConstraintProto&
interval) {
1276 bool NoOverlap2DConstraintIsFeasible(
const CpModelProto&
model,
1277 const ConstraintProto&
ct) {
1278 const auto& arg =
ct.no_overlap_2d();
1281 std::vector<std::pair<
const IntervalConstraintProto*
const,
1282 const IntervalConstraintProto*
const>>
1283 enforced_intervals_xy;
1285 const int num_intervals = arg.x_intervals_size();
1286 CHECK_EQ(arg.y_intervals_size(), num_intervals);
1287 for (
int i = 0; i < num_intervals; ++i) {
1288 const ConstraintProto& x =
model.constraints(arg.x_intervals(i));
1289 const ConstraintProto& y =
model.constraints(arg.y_intervals(i));
1290 if (ConstraintIsEnforced(x) && ConstraintIsEnforced(y) &&
1291 (!arg.boxes_with_null_area_can_overlap() ||
1292 (!IntervalIsEmpty(x.interval()) &&
1293 !IntervalIsEmpty(y.interval())))) {
1294 enforced_intervals_xy.push_back({&x.interval(), &y.interval()});
1298 const int num_enforced_intervals = enforced_intervals_xy.size();
1299 for (
int i = 0; i < num_enforced_intervals; ++i) {
1300 for (
int j = i + 1; j < num_enforced_intervals; ++j) {
1301 const auto& xi = *enforced_intervals_xy[i].first;
1302 const auto& yi = *enforced_intervals_xy[i].second;
1303 const auto& xj = *enforced_intervals_xy[j].first;
1304 const auto& yj = *enforced_intervals_xy[j].second;
1305 if (!IntervalsAreDisjoint(xi, xj) && !IntervalsAreDisjoint(yi, yj) &&
1306 !IntervalIsEmpty(xi) && !IntervalIsEmpty(xj) &&
1307 !IntervalIsEmpty(yi) && !IntervalIsEmpty(yj)) {
1308 VLOG(1) <<
"Interval " << i <<
"(x=[" << IntervalStart(xi) <<
", "
1309 << IntervalEnd(xi) <<
"], y=[" << IntervalStart(yi) <<
", "
1310 << IntervalEnd(yi) <<
"]) and " << j <<
"(x=["
1311 << IntervalStart(xj) <<
", " << IntervalEnd(xj) <<
"], y=["
1312 << IntervalStart(yj) <<
", " << IntervalEnd(yj)
1313 <<
"]) are not disjoint.";
1321 bool CumulativeConstraintIsFeasible(
const CpModelProto&
model,
1322 const ConstraintProto&
ct) {
1324 const int64_t
capacity = LinearExpressionValue(
ct.cumulative().capacity());
1325 const int num_intervals =
ct.cumulative().intervals_size();
1326 absl::flat_hash_map<int64_t, int64_t> usage;
1327 for (
int i = 0; i < num_intervals; ++i) {
1328 const ConstraintProto& interval_constraint =
1329 model.constraints(
ct.cumulative().intervals(i));
1330 if (ConstraintIsEnforced(interval_constraint)) {
1331 const IntervalConstraintProto&
interval =
1332 interval_constraint.interval();
1334 const int64_t duration = IntervalSize(
interval);
1336 LinearExpressionValue(
ct.cumulative().demands(i));
1337 for (int64_t t =
start; t <
start + duration; ++t) {
1340 VLOG(1) <<
"time: " << t <<
" usage: " << usage[t]
1350 bool ElementConstraintIsFeasible(
const ConstraintProto&
ct) {
1351 if (
ct.element().vars().empty())
return false;
1353 if (index < 0 || index >=
ct.element().vars_size())
return false;
1357 bool TableConstraintIsFeasible(
const ConstraintProto&
ct) {
1358 const int size =
ct.table().vars_size();
1359 if (size == 0)
return true;
1360 for (
int row_start = 0; row_start <
ct.table().values_size();
1361 row_start += size) {
1363 while (
Value(
ct.table().vars(i)) ==
ct.table().values(row_start + i)) {
1365 if (i == size)
return !
ct.table().negated();
1368 return ct.table().negated();
1371 bool AutomatonConstraintIsFeasible(
const ConstraintProto&
ct) {
1373 absl::flat_hash_map<std::pair<int64_t, int64_t>, int64_t> transition_map;
1374 const int num_transitions =
ct.automaton().transition_tail().size();
1375 for (
int i = 0; i < num_transitions; ++i) {
1376 transition_map[{
ct.automaton().transition_tail(i),
1377 ct.automaton().transition_label(i)}] =
1378 ct.automaton().transition_head(i);
1382 int64_t current_state =
ct.automaton().starting_state();
1383 const int num_steps =
ct.automaton().vars_size();
1384 for (
int i = 0; i < num_steps; ++i) {
1385 const std::pair<int64_t, int64_t> key = {current_state,
1386 Value(
ct.automaton().vars(i))};
1387 if (!transition_map.contains(key)) {
1390 current_state = transition_map[key];
1394 for (
const int64_t
final :
ct.automaton().final_states()) {
1395 if (current_state ==
final)
return true;
1400 bool CircuitConstraintIsFeasible(
const ConstraintProto&
ct) {
1403 const int num_arcs =
ct.circuit().tails_size();
1404 absl::flat_hash_set<int>
nodes;
1405 absl::flat_hash_map<int, int> nexts;
1406 for (
int i = 0; i < num_arcs; ++i) {
1407 const int tail =
ct.circuit().tails(i);
1408 const int head =
ct.circuit().heads(i);
1411 if (LiteralIsFalse(
ct.circuit().literals(i)))
continue;
1412 if (nexts.contains(
tail)) {
1413 VLOG(1) <<
"Node with two outgoing arcs";
1422 for (
const int node :
nodes) {
1423 if (!nexts.contains(node)) {
1424 VLOG(1) <<
"Node with no next: " << node;
1427 if (nexts[node] == node)
continue;
1431 if (cycle_size == 0)
return true;
1435 absl::flat_hash_set<int> visited;
1436 int current = in_cycle;
1437 int num_visited = 0;
1438 while (!visited.contains(current)) {
1440 visited.insert(current);
1441 current = nexts[current];
1443 if (current != in_cycle) {
1444 VLOG(1) <<
"Rho shape";
1447 if (num_visited != cycle_size) {
1448 VLOG(1) <<
"More than one cycle";
1450 return num_visited == cycle_size;
1453 bool RoutesConstraintIsFeasible(
const ConstraintProto&
ct) {
1454 const int num_arcs =
ct.routes().tails_size();
1455 int num_used_arcs = 0;
1456 int num_self_arcs = 0;
1458 std::vector<int> tail_to_head;
1459 std::vector<int> depot_nexts;
1460 for (
int i = 0; i < num_arcs; ++i) {
1461 const int tail =
ct.routes().tails(i);
1462 const int head =
ct.routes().heads(i);
1465 tail_to_head.resize(num_nodes, -1);
1466 if (LiteralIsTrue(
ct.routes().literals(i))) {
1468 if (
tail == 0)
return false;
1474 depot_nexts.push_back(
head);
1476 if (tail_to_head[
tail] != -1)
return false;
1483 if (num_nodes == 0)
return true;
1487 for (
int start : depot_nexts) {
1489 while (
start != 0) {
1490 if (tail_to_head[
start] == -1)
return false;
1496 if (count != num_used_arcs) {
1497 VLOG(1) <<
"count: " << count <<
" != num_used_arcs:" << num_used_arcs;
1505 if (count - depot_nexts.size() + 1 + num_self_arcs != num_nodes) {
1506 VLOG(1) <<
"Not all nodes are covered!";
1513 bool InverseConstraintIsFeasible(
const ConstraintProto&
ct) {
1514 const int num_variables =
ct.inverse().f_direct_size();
1515 if (num_variables !=
ct.inverse().f_inverse_size())
return false;
1517 for (
int i = 0; i < num_variables; i++) {
1518 const int fi =
Value(
ct.inverse().f_direct(i));
1519 if (fi < 0 || num_variables <= fi)
return false;
1520 if (i !=
Value(
ct.inverse().f_inverse(fi)))
return false;
1525 bool ReservoirConstraintIsFeasible(
const ConstraintProto&
ct) {
1526 const int num_variables =
ct.reservoir().time_exprs_size();
1527 const int64_t min_level =
ct.reservoir().min_level();
1528 const int64_t max_level =
ct.reservoir().max_level();
1529 absl::btree_map<int64_t, int64_t> deltas;
1530 const bool has_active_variables =
ct.reservoir().active_literals_size() > 0;
1531 for (
int i = 0; i < num_variables; i++) {
1532 const int64_t
time = LinearExpressionValue(
ct.reservoir().time_exprs(i));
1533 if (!has_active_variables ||
1534 Value(
ct.reservoir().active_literals(i)) == 1) {
1535 const int64_t level =
1536 LinearExpressionValue(
ct.reservoir().level_changes(i));
1537 deltas[
time] += level;
1540 int64_t current_level = 0;
1541 for (
const auto&
delta : deltas) {
1542 current_level +=
delta.second;
1543 if (current_level < min_level || current_level > max_level) {
1544 VLOG(1) <<
"Reservoir level " << current_level
1545 <<
" is out of bounds at time" <<
delta.first;
1553 const std::vector<int64_t> variable_values_;
1559 absl::Span<const int64_t> variable_values,
1560 const CpModelProto* mapping_proto,
1561 const std::vector<int>* postsolve_mapping) {
1562 if (variable_values.size() !=
model.variables_size()) {
1563 VLOG(1) <<
"Wrong number of variables (" << variable_values.size()
1564 <<
") in the solution vector. It should be "
1565 <<
model.variables_size() <<
".";
1570 for (
int i = 0; i <
model.variables_size(); ++i) {
1572 VLOG(1) <<
"Variable #" << i <<
" has value " << variable_values[i]
1573 <<
" which do not fall in its domain: "
1579 CHECK_EQ(variable_values.size(),
model.variables_size());
1580 ConstraintChecker checker(variable_values);
1582 for (
int c = 0; c <
model.constraints_size(); ++c) {
1583 const ConstraintProto&
ct =
model.constraints(c);
1585 if (!checker.ConstraintIsEnforced(
ct))
continue;
1587 bool is_feasible =
true;
1588 const ConstraintProto::ConstraintCase type =
ct.constraint_case();
1590 case ConstraintProto::ConstraintCase::kBoolOr:
1591 is_feasible = checker.BoolOrConstraintIsFeasible(
ct);
1593 case ConstraintProto::ConstraintCase::kBoolAnd:
1594 is_feasible = checker.BoolAndConstraintIsFeasible(
ct);
1596 case ConstraintProto::ConstraintCase::kAtMostOne:
1597 is_feasible = checker.AtMostOneConstraintIsFeasible(
ct);
1599 case ConstraintProto::ConstraintCase::kExactlyOne:
1600 is_feasible = checker.ExactlyOneConstraintIsFeasible(
ct);
1602 case ConstraintProto::ConstraintCase::kBoolXor:
1603 is_feasible = checker.BoolXorConstraintIsFeasible(
ct);
1605 case ConstraintProto::ConstraintCase::kLinear:
1606 is_feasible = checker.LinearConstraintIsFeasible(
ct);
1608 case ConstraintProto::ConstraintCase::kIntProd:
1609 is_feasible = checker.IntProdConstraintIsFeasible(
ct);
1611 case ConstraintProto::ConstraintCase::kIntDiv:
1612 is_feasible = checker.IntDivConstraintIsFeasible(
ct);
1614 case ConstraintProto::ConstraintCase::kIntMod:
1615 is_feasible = checker.IntModConstraintIsFeasible(
ct);
1617 case ConstraintProto::ConstraintCase::kLinMax:
1618 is_feasible = checker.LinMaxConstraintIsFeasible(
ct);
1620 case ConstraintProto::ConstraintCase::kAllDiff:
1621 is_feasible = checker.AllDiffConstraintIsFeasible(
ct);
1623 case ConstraintProto::ConstraintCase::kInterval:
1624 if (!checker.IntervalConstraintIsFeasible(
ct)) {
1625 if (
ct.interval().has_start()) {
1631 LOG(ERROR) <<
"Warning, an interval constraint was likely used "
1632 "without a corresponding linear constraint linking "
1633 "its start, size and end.";
1635 is_feasible =
false;
1639 case ConstraintProto::ConstraintCase::kNoOverlap:
1640 is_feasible = checker.NoOverlapConstraintIsFeasible(
model,
ct);
1642 case ConstraintProto::ConstraintCase::kNoOverlap2D:
1643 is_feasible = checker.NoOverlap2DConstraintIsFeasible(
model,
ct);
1645 case ConstraintProto::ConstraintCase::kCumulative:
1646 is_feasible = checker.CumulativeConstraintIsFeasible(
model,
ct);
1648 case ConstraintProto::ConstraintCase::kElement:
1649 is_feasible = checker.ElementConstraintIsFeasible(
ct);
1651 case ConstraintProto::ConstraintCase::kTable:
1652 is_feasible = checker.TableConstraintIsFeasible(
ct);
1654 case ConstraintProto::ConstraintCase::kAutomaton:
1655 is_feasible = checker.AutomatonConstraintIsFeasible(
ct);
1657 case ConstraintProto::ConstraintCase::kCircuit:
1658 is_feasible = checker.CircuitConstraintIsFeasible(
ct);
1660 case ConstraintProto::ConstraintCase::kRoutes:
1661 is_feasible = checker.RoutesConstraintIsFeasible(
ct);
1663 case ConstraintProto::ConstraintCase::kInverse:
1664 is_feasible = checker.InverseConstraintIsFeasible(
ct);
1666 case ConstraintProto::ConstraintCase::kReservoir:
1667 is_feasible = checker.ReservoirConstraintIsFeasible(
ct);
1669 case ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET:
1678 VLOG(1) <<
"Failing constraint #" << c <<
" : "
1680 if (mapping_proto !=
nullptr && postsolve_mapping !=
nullptr) {
1681 std::vector<int> reverse_map(mapping_proto->variables().size(), -1);
1682 for (
int var = 0;
var < postsolve_mapping->size(); ++
var) {
1683 reverse_map[(*postsolve_mapping)[
var]] =
var;
1686 VLOG(1) <<
"var: " <<
var <<
" mapped_to: " << reverse_map[
var]
1687 <<
" value: " << variable_values[
var] <<
" initial_domain: "
1689 <<
" postsolved_domain: "
1694 VLOG(1) <<
"var: " <<
var <<
" value: " << variable_values[
var];
1706 if (
model.has_objective()) {
1707 int64_t inner_objective = 0;
1708 const int num_variables =
model.objective().coeffs_size();
1709 for (
int i = 0; i < num_variables; ++i) {
1710 inner_objective += checker.Value(
model.objective().vars(i)) *
1711 model.objective().coeffs(i);
1713 if (!
model.objective().domain().empty()) {
1715 VLOG(1) <<
"Objective value " << inner_objective <<
" not in domain! "
1720 double factor =
model.objective().scaling_factor();
1721 if (factor == 0.0) factor = 1.0;
1722 const double scaled_objective =
1724 (
static_cast<double>(inner_objective) +
model.objective().offset());
1725 VLOG(2) <<
"Checker inner objective = " << inner_objective;
1726 VLOG(2) <<
"Checker scaled objective = " << scaled_objective;
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)
absl::Status ValidateLinearExpression(const LinearExpressionProto &expression, const IdNameBiMap &variable_universe)
std::vector< int > UsedVariables(const ConstraintProto &ct)
bool RefIsPositive(int ref)
std::vector< int > UsedIntervals(const ConstraintProto &ct)
std::string ValidateInputCpModel(const SatParameters ¶ms, const CpModelProto &model)
bool SolutionIsFeasible(const CpModelProto &model, absl::Span< const int64_t > variable_values, const CpModelProto *mapping_proto, const std::vector< int > *postsolve_mapping)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
bool DomainInProtoContains(const ProtoWithDomain &proto, int64_t value)
std::string ValidateCpModel(const CpModelProto &model, bool after_presolve)
bool PossibleIntegerOverflow(const CpModelProto &model, absl::Span< const int > vars, absl::Span< const int64_t > coeffs, int64_t offset)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
IndexReferences GetReferencesUsedByConstraint(const ConstraintProto &ct)
std::string ConstraintCaseName(ConstraintProto::ConstraintCase constraint_case)
Collection of objects used to extend the Constraint Solver library.
bool AtMinOrMaxInt64(int64_t x)
int64_t CapAdd(int64_t x, int64_t y)
std::string ProtobufShortDebugString(const P &message)
int64_t CapProd(int64_t x, int64_t y)
int64_t CapAbs(int64_t v)
std::string ProtobufDebugString(const P &message)
bool IntervalsAreSortedAndNonAdjacent(absl::Span< const ClosedInterval > intervals)
Returns true iff we have:
static int input(yyscan_t yyscanner)
std::optional< int64_t > end
#define VLOG(verboselevel)