20 #include "absl/container/flat_hash_map.h"
21 #include "absl/container/flat_hash_set.h"
22 #include "absl/strings/str_cat.h"
41 #define RETURN_IF_NOT_EMPTY(statement) \
43 const std::string error_message = statement; \
44 if (!error_message.empty()) return error_message; \
47 template <
typename ProtoWithDomain>
48 bool DomainInProtoIsValid(
const ProtoWithDomain&
proto) {
49 if (
proto.domain().size() % 2)
return false;
50 std::vector<ClosedInterval> domain;
51 for (
int i = 0; i <
proto.domain_size(); i += 2) {
52 if (
proto.domain(i) >
proto.domain(i + 1))
return false;
53 domain.push_back({
proto.domain(i),
proto.domain(i + 1)});
58 bool VariableReferenceIsValid(
const CpModelProto&
model,
int reference) {
60 if (reference >=
model.variables_size())
return false;
61 return reference >= -
static_cast<int>(
model.variables_size());
64 bool LiteralReferenceIsValid(
const CpModelProto&
model,
int reference) {
65 if (!VariableReferenceIsValid(
model, reference))
return false;
67 const int64 min_domain = var_proto.domain(0);
68 const int64 max_domain = var_proto.domain(var_proto.domain_size() - 1);
69 return min_domain >= 0 && max_domain <= 1;
72 std::string ValidateIntegerVariable(
const CpModelProto&
model,
int v) {
73 const IntegerVariableProto&
proto =
model.variables(v);
74 if (
proto.domain_size() == 0) {
75 return absl::StrCat(
"var #", v,
78 if (
proto.domain_size() % 2 != 0) {
79 return absl::StrCat(
"var #", v,
" has an odd domain() size: ",
82 if (!DomainInProtoIsValid(
proto)) {
83 return absl::StrCat(
"var #", v,
" has and invalid domain() format: ",
92 if (lb < kint64min + 2 || ub >
kint64max - 1) {
94 "var #", v,
" domain do not fall in [kint64min + 2, kint64max - 1]. ",
103 " has a domain that is too large, i.e. |UB - LB| overflow an int64: ",
110 std::string ValidateArgumentReferencesInConstraint(
const CpModelProto&
model,
112 const ConstraintProto&
ct =
model.constraints(c);
114 for (
const int v : references.variables) {
115 if (!VariableReferenceIsValid(
model, v)) {
116 return absl::StrCat(
"Out of bound integer variable ", v,
117 " in constraint #", c,
" : ",
121 for (
const int lit :
ct.enforcement_literal()) {
122 if (!LiteralReferenceIsValid(
model, lit)) {
123 return absl::StrCat(
"Invalid enforcement literal ", lit,
124 " in constraint #", c,
" : ",
128 for (
const int lit : references.literals) {
129 if (!LiteralReferenceIsValid(
model, lit)) {
130 return absl::StrCat(
"Invalid literal ", lit,
" in constraint #", c,
" : ",
135 if (i < 0 || i >=
model.constraints_size()) {
136 return absl::StrCat(
"Out of bound interval ", i,
" in constraint #", c,
139 if (
model.constraints(i).constraint_case() !=
140 ConstraintProto::ConstraintCase::kInterval) {
143 " does not refer to an interval constraint. Problematic constraint #",
150 template <
class LinearExpressionProto>
151 bool PossibleIntegerOverflow(
const CpModelProto&
model,
152 const LinearExpressionProto&
proto) {
155 for (
int i = 0; i <
proto.vars_size(); ++i) {
156 const int ref =
proto.vars(i);
158 const int64 min_domain = var_proto.domain(0);
159 const int64 max_domain = var_proto.domain(var_proto.domain_size() - 1);
169 for (
const int64 v : {prod1, prod2, sum_min, sum_max}) {
176 if (sum_min < 0 && sum_min +
kint64max < sum_max) {
182 std::string ValidateIntervalConstraint(
const CpModelProto&
model,
183 const ConstraintProto&
ct) {
184 const IntervalConstraintProto& arg =
ct.interval();
185 if (arg.size() < 0) {
186 const IntegerVariableProto& size_var_proto =
188 if (size_var_proto.domain(size_var_proto.domain_size() - 1) > 0) {
194 const IntegerVariableProto& size_var_proto =
model.variables(arg.size());
195 if (size_var_proto.domain(0) < 0) {
204 std::string ValidateLinearConstraint(
const CpModelProto&
model,
205 const ConstraintProto&
ct) {
206 const LinearConstraintProto& arg =
ct.linear();
207 if (PossibleIntegerOverflow(
model, arg)) {
208 return "Possible integer overflow in constraint: " +
214 std::string ValidateLinearExpression(
const CpModelProto&
model,
215 const LinearExpressionProto& expr) {
216 if (expr.coeffs_size() != expr.vars_size()) {
217 return absl::StrCat(
"coeffs_size() != vars_size() in linear expression: ",
220 if (PossibleIntegerOverflow(
model, expr)) {
221 return absl::StrCat(
"Possible overflow in linear expression: ",
227 std::string ValidateCircuitConstraint(
const CpModelProto&
model,
228 const ConstraintProto&
ct) {
229 const int size =
ct.circuit().tails().size();
230 if (
ct.circuit().heads().size() != size ||
231 ct.circuit().literals().size() != size) {
232 return absl::StrCat(
"Wrong field sizes in circuit: ",
238 std::string ValidateRoutesConstraint(
const CpModelProto&
model,
239 const ConstraintProto&
ct) {
240 const int size =
ct.routes().tails().size();
241 if (
ct.routes().heads().size() != size ||
242 ct.routes().literals().size() != size) {
243 return absl::StrCat(
"Wrong field sizes in routes: ",
249 std::string ValidateReservoirConstraint(
const CpModelProto&
model,
250 const ConstraintProto&
ct) {
251 if (
ct.enforcement_literal_size() > 0) {
252 return "Reservoir does not support enforcement literals.";
254 if (
ct.reservoir().times().size() !=
ct.reservoir().demands().size()) {
255 return absl::StrCat(
"Times and demands fields must be of the same size: ",
258 for (
const int t :
ct.reservoir().times()) {
259 const IntegerVariableProto&
time =
model.variables(t);
262 return absl::StrCat(
"Time variables must be >= 0 in constraint ",
271 return "Possible integer overflow in constraint: " +
275 if (
ct.reservoir().actives_size() > 0 &&
276 ct.reservoir().actives_size() !=
ct.reservoir().times_size()) {
277 return "Wrong array length of actives variables";
279 if (
ct.reservoir().demands_size() > 0 &&
280 ct.reservoir().demands_size() !=
ct.reservoir().times_size()) {
281 return "Wrong array length of demands variables";
286 std::string ValidateCircuitCoveringConstraint(
const ConstraintProto&
ct) {
287 const int num_nodes =
ct.circuit_covering().nexts_size();
288 for (
const int d :
ct.circuit_covering().distinguished_nodes()) {
289 if (d < 0 || d >= num_nodes) {
290 return absl::StrCat(
"Distinguished node ", d,
" not in [0, ", num_nodes,
298 std::string ValidateIntModConstraint(
const CpModelProto&
model,
299 const ConstraintProto&
ct) {
300 if (
ct.int_mod().vars().size() != 2) {
301 return absl::StrCat(
"An int_mod constraint should have exactly 2 terms: ",
304 const IntegerVariableProto& mod_proto =
model.variables(
ct.int_mod().vars(1));
305 if (mod_proto.domain(0) <= 0) {
307 "An int_mod must have a strictly positive modulo argument: ",
313 std::string ValidateObjective(
const CpModelProto&
model,
314 const CpObjectiveProto& obj) {
315 if (!DomainInProtoIsValid(obj)) {
316 return absl::StrCat(
"The objective has and invalid domain() format: ",
319 if (obj.vars().size() != obj.coeffs().size()) {
320 return absl::StrCat(
"vars and coeffs size do not match in objective: ",
323 for (
const int v : obj.vars()) {
324 if (!VariableReferenceIsValid(
model, v)) {
325 return absl::StrCat(
"Out of bound integer variable ", v,
329 if (PossibleIntegerOverflow(
model, obj)) {
330 return "Possible integer overflow in objective: " +
336 std::string ValidateSearchStrategies(
const CpModelProto&
model) {
337 for (
const DecisionStrategyProto& strategy :
model.search_strategy()) {
338 for (
const int ref : strategy.variables()) {
339 if (!VariableReferenceIsValid(
model, ref)) {
340 return absl::StrCat(
"Invalid variable reference in strategy: ",
344 for (
const auto& transformation : strategy.transformations()) {
345 if (transformation.positive_coeff() <= 0) {
346 return absl::StrCat(
"Affine transformation coeff should be positive: ",
349 if (!VariableReferenceIsValid(
model, transformation.var())) {
351 "Invalid variable reference in affine transformation: ",
359 std::string ValidateSolutionHint(
const CpModelProto&
model) {
360 if (!
model.has_solution_hint())
return "";
361 const auto& hint =
model.solution_hint();
362 if (hint.vars().size() != hint.values().size()) {
363 return "Invalid solution hint: vars and values do not have the same size.";
365 for (
const int ref : hint.vars()) {
366 if (!VariableReferenceIsValid(
model, ref)) {
367 return absl::StrCat(
"Invalid variable reference in solution hint: ", ref);
376 for (
int v = 0; v <
model.variables_size(); ++v) {
379 for (
int c = 0; c <
model.constraints_size(); ++c) {
384 bool support_enforcement =
false;
388 const ConstraintProto&
ct =
model.constraints(c);
389 const ConstraintProto::ConstraintCase type =
ct.constraint_case();
391 case ConstraintProto::ConstraintCase::kIntDiv:
392 if (
ct.int_div().vars().size() != 2) {
394 "An int_div constraint should have exactly 2 terms: ",
398 case ConstraintProto::ConstraintCase::kIntMod:
401 case ConstraintProto::ConstraintCase::kBoolOr:
402 support_enforcement =
true;
404 case ConstraintProto::ConstraintCase::kBoolAnd:
405 support_enforcement =
true;
407 case ConstraintProto::ConstraintCase::kLinear:
408 support_enforcement =
true;
409 if (!DomainInProtoIsValid(
ct.linear())) {
410 return absl::StrCat(
"Invalid domain in constraint #", c,
" : ",
413 if (
ct.linear().coeffs_size() !=
ct.linear().vars_size()) {
414 return absl::StrCat(
"coeffs_size() != vars_size() in constraint #", c,
419 case ConstraintProto::ConstraintCase::kLinMax: {
420 const std::string target_error =
421 ValidateLinearExpression(
model,
ct.lin_min().target());
422 if (!target_error.empty())
return target_error;
423 for (
int i = 0; i <
ct.lin_max().exprs_size(); ++i) {
424 const std::string expr_error =
425 ValidateLinearExpression(
model,
ct.lin_max().exprs(i));
426 if (!expr_error.empty())
return expr_error;
430 case ConstraintProto::ConstraintCase::kLinMin: {
431 const std::string target_error =
432 ValidateLinearExpression(
model,
ct.lin_min().target());
433 if (!target_error.empty())
return target_error;
434 for (
int i = 0; i <
ct.lin_min().exprs_size(); ++i) {
435 const std::string expr_error =
436 ValidateLinearExpression(
model,
ct.lin_min().exprs(i));
437 if (!expr_error.empty())
return expr_error;
442 case ConstraintProto::ConstraintCase::kInterval:
443 support_enforcement =
true;
446 case ConstraintProto::ConstraintCase::kCumulative:
447 if (
ct.cumulative().intervals_size() !=
448 ct.cumulative().demands_size()) {
450 "intervals_size() != demands_size() in constraint #", c,
" : ",
454 case ConstraintProto::ConstraintCase::kInverse:
455 if (
ct.inverse().f_direct().size() !=
ct.inverse().f_inverse().size()) {
456 return absl::StrCat(
"Non-matching fields size in inverse: ",
460 case ConstraintProto::ConstraintCase::kCircuit:
463 case ConstraintProto::ConstraintCase::kRoutes:
466 case ConstraintProto::ConstraintCase::kReservoir:
469 case ConstraintProto::ConstraintCase::kCircuitCovering:
479 if (!support_enforcement && !
ct.enforcement_literal().empty()) {
480 for (
const int ref :
ct.enforcement_literal()) {
483 if (domain.
Size() != 1) {
485 "Enforcement literal not supported in constraint: ",
491 if (
model.has_objective()) {
496 for (
const int ref :
model.assumptions()) {
497 if (!LiteralReferenceIsValid(
model, ref)) {
498 return absl::StrCat(
"Invalid literal reference ", ref,
499 " in the 'assumptions' field.");
505 #undef RETURN_IF_NOT_EMPTY
513 class ConstraintChecker {
515 explicit ConstraintChecker(
const std::vector<int64>& variable_values)
516 : variable_values_(variable_values) {}
518 bool LiteralIsTrue(
int l)
const {
519 if (l >= 0)
return variable_values_[l] != 0;
520 return variable_values_[-l - 1] == 0;
523 bool LiteralIsFalse(
int l)
const {
return !LiteralIsTrue(l); }
526 if (
var >= 0)
return variable_values_[
var];
527 return -variable_values_[-
var - 1];
530 bool ConstraintIsEnforced(
const ConstraintProto&
ct) {
531 for (
const int lit :
ct.enforcement_literal()) {
532 if (LiteralIsFalse(lit))
return false;
537 bool BoolOrConstraintIsFeasible(
const ConstraintProto&
ct) {
538 for (
const int lit :
ct.bool_or().literals()) {
539 if (LiteralIsTrue(lit))
return true;
544 bool BoolAndConstraintIsFeasible(
const ConstraintProto&
ct) {
545 for (
const int lit :
ct.bool_and().literals()) {
546 if (LiteralIsFalse(lit))
return false;
551 bool AtMostOneConstraintIsFeasible(
const ConstraintProto&
ct) {
552 int num_true_literals = 0;
553 for (
const int lit :
ct.at_most_one().literals()) {
554 if (LiteralIsTrue(lit)) ++num_true_literals;
556 return num_true_literals <= 1;
559 bool BoolXorConstraintIsFeasible(
const ConstraintProto&
ct) {
561 for (
const int lit :
ct.bool_xor().literals()) {
562 sum ^= LiteralIsTrue(lit) ? 1 : 0;
567 bool LinearConstraintIsFeasible(
const ConstraintProto&
ct) {
569 const int num_variables =
ct.linear().coeffs_size();
570 for (
int i = 0; i < num_variables; ++i) {
571 sum +=
Value(
ct.linear().vars(i)) *
ct.linear().coeffs(i);
576 bool IntMaxConstraintIsFeasible(
const ConstraintProto&
ct) {
579 for (
int i = 0; i <
ct.int_max().vars_size(); ++i) {
582 return max == actual_max;
585 int64 LinearExpressionValue(
const LinearExpressionProto& expr) {
586 int64 sum = expr.offset();
587 const int num_variables = expr.vars_size();
588 for (
int i = 0; i < num_variables; ++i) {
589 sum +=
Value(expr.vars(i)) * expr.coeffs(i);
594 bool LinMaxConstraintIsFeasible(
const ConstraintProto&
ct) {
595 const int64 max = LinearExpressionValue(
ct.lin_max().target());
597 for (
int i = 0; i <
ct.lin_max().exprs_size(); ++i) {
598 const int64 expr_value = LinearExpressionValue(
ct.lin_max().exprs(i));
599 actual_max =
std::max(actual_max, expr_value);
601 return max == actual_max;
604 bool IntProdConstraintIsFeasible(
const ConstraintProto&
ct) {
606 int64 actual_prod = 1;
607 for (
int i = 0; i <
ct.int_prod().vars_size(); ++i) {
608 actual_prod *=
Value(
ct.int_prod().vars(i));
610 return prod == actual_prod;
613 bool IntDivConstraintIsFeasible(
const ConstraintProto&
ct) {
614 return Value(
ct.int_div().target()) ==
618 bool IntModConstraintIsFeasible(
const ConstraintProto&
ct) {
619 return Value(
ct.int_mod().target()) ==
623 bool IntMinConstraintIsFeasible(
const ConstraintProto&
ct) {
626 for (
int i = 0; i <
ct.int_min().vars_size(); ++i) {
629 return min == actual_min;
632 bool LinMinConstraintIsFeasible(
const ConstraintProto&
ct) {
633 const int64 min = LinearExpressionValue(
ct.lin_min().target());
635 for (
int i = 0; i <
ct.lin_min().exprs_size(); ++i) {
636 const int64 expr_value = LinearExpressionValue(
ct.lin_min().exprs(i));
637 actual_min =
std::min(actual_min, expr_value);
639 return min == actual_min;
642 bool AllDiffConstraintIsFeasible(
const ConstraintProto&
ct) {
643 absl::flat_hash_set<int64> values;
644 for (
const int v :
ct.all_diff().vars()) {
646 values.insert(
Value(v));
651 bool IntervalConstraintIsFeasible(
const ConstraintProto&
ct) {
653 if (size < 0)
return false;
654 return Value(
ct.interval().start()) + size ==
Value(
ct.interval().end());
657 bool NoOverlapConstraintIsFeasible(
const CpModelProto&
model,
658 const ConstraintProto&
ct) {
659 std::vector<std::pair<int64, int64>> start_durations_pairs;
660 for (
const int i :
ct.no_overlap().intervals()) {
661 const ConstraintProto& interval_constraint =
model.constraints(i);
662 if (ConstraintIsEnforced(interval_constraint)) {
663 const IntervalConstraintProto&
interval =
664 interval_constraint.interval();
665 start_durations_pairs.push_back(
669 std::sort(start_durations_pairs.begin(), start_durations_pairs.end());
671 for (
const auto pair : start_durations_pairs) {
672 if (pair.first < previous_end)
return false;
673 previous_end = pair.first + pair.second;
678 bool IntervalsAreDisjoint(
const IntervalConstraintProto& interval1,
679 const IntervalConstraintProto& interval2) {
680 return Value(interval1.end()) <=
Value(interval2.start()) ||
681 Value(interval2.end()) <=
Value(interval1.start());
684 bool IntervalIsEmpty(
const IntervalConstraintProto&
interval) {
688 bool NoOverlap2DConstraintIsFeasible(
const CpModelProto&
model,
689 const ConstraintProto&
ct) {
690 const auto& arg =
ct.no_overlap_2d();
693 std::vector<std::pair<
const IntervalConstraintProto*
const,
694 const IntervalConstraintProto*
const>>
695 enforced_intervals_xy;
697 const int num_intervals = arg.x_intervals_size();
698 CHECK_EQ(arg.y_intervals_size(), num_intervals);
699 for (
int i = 0; i < num_intervals; ++i) {
700 const ConstraintProto& x =
model.constraints(arg.x_intervals(i));
701 const ConstraintProto& y =
model.constraints(arg.y_intervals(i));
702 if (ConstraintIsEnforced(x) && ConstraintIsEnforced(y) &&
703 (!arg.boxes_with_null_area_can_overlap() ||
704 (!IntervalIsEmpty(x.interval()) &&
705 !IntervalIsEmpty(y.interval())))) {
706 enforced_intervals_xy.push_back({&x.interval(), &y.interval()});
710 const int num_enforced_intervals = enforced_intervals_xy.size();
711 for (
int i = 0; i < num_enforced_intervals; ++i) {
712 for (
int j = i + 1; j < num_enforced_intervals; ++j) {
713 const auto& xi = *enforced_intervals_xy[i].first;
714 const auto& yi = *enforced_intervals_xy[i].second;
715 const auto& xj = *enforced_intervals_xy[j].first;
716 const auto& yj = *enforced_intervals_xy[j].second;
717 if (!IntervalsAreDisjoint(xi, xj) && !IntervalsAreDisjoint(yi, yj) &&
718 !IntervalIsEmpty(xi) && !IntervalIsEmpty(xj) &&
719 !IntervalIsEmpty(yi) && !IntervalIsEmpty(yj)) {
720 VLOG(1) <<
"Interval " << i <<
"(x=[" <<
Value(xi.start()) <<
", "
721 <<
Value(xi.end()) <<
"], y=[" <<
Value(yi.start()) <<
", "
722 <<
Value(yi.end()) <<
"]) and " << j <<
"("
723 <<
"(x=[" <<
Value(xj.start()) <<
", " <<
Value(xj.end())
724 <<
"], y=[" <<
Value(yj.start()) <<
", " <<
Value(yj.end())
725 <<
"]) are not disjoint.";
733 bool CumulativeConstraintIsFeasible(
const CpModelProto&
model,
734 const ConstraintProto&
ct) {
737 const int num_intervals =
ct.cumulative().intervals_size();
738 absl::flat_hash_map<int64, int64> usage;
739 for (
int i = 0; i < num_intervals; ++i) {
740 const ConstraintProto& interval_constraint =
741 model.constraints(
ct.cumulative().intervals(i));
742 if (ConstraintIsEnforced(interval_constraint)) {
743 const IntervalConstraintProto&
interval =
744 interval_constraint.interval();
748 for (
int64 t = start; t < start + duration; ++t) {
750 if (usage[t] >
capacity)
return false;
757 bool ElementConstraintIsFeasible(
const ConstraintProto&
ct) {
762 bool TableConstraintIsFeasible(
const ConstraintProto&
ct) {
763 const int size =
ct.table().vars_size();
764 if (size == 0)
return true;
765 for (
int row_start = 0; row_start <
ct.table().values_size();
768 while (
Value(
ct.table().vars(i)) ==
ct.table().values(row_start + i)) {
770 if (i == size)
return !
ct.table().negated();
773 return ct.table().negated();
776 bool AutomatonConstraintIsFeasible(
const ConstraintProto&
ct) {
778 absl::flat_hash_map<std::pair<int64, int64>,
int64> transition_map;
779 const int num_transitions =
ct.automaton().transition_tail().size();
780 for (
int i = 0; i < num_transitions; ++i) {
781 transition_map[{
ct.automaton().transition_tail(i),
782 ct.automaton().transition_label(i)}] =
783 ct.automaton().transition_head(i);
787 int64 current_state =
ct.automaton().starting_state();
788 const int num_steps =
ct.automaton().vars_size();
789 for (
int i = 0; i < num_steps; ++i) {
790 const std::pair<int64, int64> key = {current_state,
791 Value(
ct.automaton().vars(i))};
795 current_state = transition_map[key];
799 for (
const int64 final :
ct.automaton().final_states()) {
800 if (current_state ==
final)
return true;
805 bool CircuitConstraintIsFeasible(
const ConstraintProto&
ct) {
808 const int num_arcs =
ct.circuit().tails_size();
809 absl::flat_hash_set<int> nodes;
810 absl::flat_hash_map<int, int> nexts;
811 for (
int i = 0; i < num_arcs; ++i) {
812 const int tail =
ct.circuit().tails(i);
813 const int head =
ct.circuit().heads(i);
816 if (LiteralIsFalse(
ct.circuit().literals(i)))
continue;
817 if (nexts.contains(
tail))
return false;
824 for (
const int node : nodes) {
825 if (!nexts.contains(node))
return false;
826 if (nexts[node] == node)
continue;
830 if (cycle_size == 0)
return true;
834 absl::flat_hash_set<int> visited;
835 int current = in_cycle;
837 while (!visited.contains(current)) {
839 visited.insert(current);
840 current = nexts[current];
842 if (current != in_cycle)
return false;
843 return num_visited == cycle_size;
846 bool RoutesConstraintIsFeasible(
const ConstraintProto&
ct) {
847 const int num_arcs =
ct.routes().tails_size();
848 int num_used_arcs = 0;
849 int num_self_arcs = 0;
851 std::vector<int> tail_to_head;
852 std::vector<int> depot_nexts;
853 for (
int i = 0; i < num_arcs; ++i) {
854 const int tail =
ct.routes().tails(i);
855 const int head =
ct.routes().heads(i);
858 tail_to_head.resize(num_nodes, -1);
859 if (LiteralIsTrue(
ct.routes().literals(i))) {
861 if (
tail == 0)
return false;
867 depot_nexts.push_back(
head);
869 if (tail_to_head[
tail] != -1)
return false;
876 if (num_nodes == 0)
return true;
880 for (
int start : depot_nexts) {
883 if (tail_to_head[start] == -1)
return false;
884 start = tail_to_head[start];
889 if (count != num_used_arcs) {
890 VLOG(1) <<
"count: " << count <<
" != num_used_arcs:" << num_used_arcs;
898 if (count - depot_nexts.size() + 1 + num_self_arcs != num_nodes) {
899 VLOG(1) <<
"Not all nodes are covered!";
906 bool CircuitCoveringConstraintIsFeasible(
const ConstraintProto&
ct) {
907 const int num_nodes =
ct.circuit_covering().nexts_size();
908 std::vector<bool> distinguished(num_nodes,
false);
909 std::vector<bool> visited(num_nodes,
false);
910 for (
const int node :
ct.circuit_covering().distinguished_nodes()) {
911 distinguished[node] =
true;
917 std::vector<int>
next(num_nodes, -1);
918 for (
const int d :
ct.circuit_covering().distinguished_nodes()) {
920 for (
int node =
Value(
ct.circuit_covering().nexts(d)); node != d;
921 node =
Value(
ct.circuit_covering().nexts(node))) {
922 if (distinguished[node])
return false;
923 CHECK(!visited[node]);
924 visited[node] =
true;
929 for (
int node = 0; node < num_nodes; node++) {
930 if (!visited[node] &&
Value(
ct.circuit_covering().nexts(node)) != node) {
937 bool InverseConstraintIsFeasible(
const ConstraintProto&
ct) {
938 const int num_variables =
ct.inverse().f_direct_size();
939 if (num_variables !=
ct.inverse().f_inverse_size())
return false;
941 for (
int i = 0; i < num_variables; i++) {
942 const int fi =
Value(
ct.inverse().f_direct(i));
943 if (fi < 0 || num_variables <= fi)
return false;
944 if (i !=
Value(
ct.inverse().f_inverse(fi)))
return false;
949 bool ReservoirConstraintIsFeasible(
const ConstraintProto&
ct) {
950 const int num_variables =
ct.reservoir().times_size();
951 const int64 min_level =
ct.reservoir().min_level();
952 const int64 max_level =
ct.reservoir().max_level();
953 std::map<int64, int64> deltas;
955 const bool has_active_variables =
ct.reservoir().actives_size() > 0;
956 for (
int i = 0; i < num_variables; i++) {
959 VLOG(1) <<
"reservoir times(" << i <<
") is negative.";
962 if (!has_active_variables ||
Value(
ct.reservoir().actives(i)) == 1) {
963 deltas[
time] +=
ct.reservoir().demands(i);
966 int64 current_level = 0;
967 for (
const auto&
delta : deltas) {
968 current_level +=
delta.second;
969 if (current_level < min_level || current_level > max_level) {
970 VLOG(1) <<
"Reservoir level " << current_level
971 <<
" is out of bounds at time" <<
delta.first;
979 std::vector<int64> variable_values_;
985 const std::vector<int64>& variable_values,
986 const CpModelProto* mapping_proto,
987 const std::vector<int>* postsolve_mapping) {
988 if (variable_values.size() !=
model.variables_size()) {
989 VLOG(1) <<
"Wrong number of variables in the solution vector";
994 for (
int i = 0; i <
model.variables_size(); ++i) {
996 VLOG(1) <<
"Variable #" << i <<
" has value " << variable_values[i]
997 <<
" which do not fall in its domain: "
1004 ConstraintChecker checker(variable_values);
1006 for (
int c = 0; c <
model.constraints_size(); ++c) {
1007 const ConstraintProto&
ct =
model.constraints(c);
1009 if (!checker.ConstraintIsEnforced(
ct))
continue;
1011 bool is_feasible =
true;
1012 const ConstraintProto::ConstraintCase type =
ct.constraint_case();
1014 case ConstraintProto::ConstraintCase::kBoolOr:
1015 is_feasible = checker.BoolOrConstraintIsFeasible(
ct);
1017 case ConstraintProto::ConstraintCase::kBoolAnd:
1018 is_feasible = checker.BoolAndConstraintIsFeasible(
ct);
1020 case ConstraintProto::ConstraintCase::kAtMostOne:
1021 is_feasible = checker.AtMostOneConstraintIsFeasible(
ct);
1023 case ConstraintProto::ConstraintCase::kBoolXor:
1024 is_feasible = checker.BoolXorConstraintIsFeasible(
ct);
1026 case ConstraintProto::ConstraintCase::kLinear:
1027 is_feasible = checker.LinearConstraintIsFeasible(
ct);
1029 case ConstraintProto::ConstraintCase::kIntProd:
1030 is_feasible = checker.IntProdConstraintIsFeasible(
ct);
1032 case ConstraintProto::ConstraintCase::kIntDiv:
1033 is_feasible = checker.IntDivConstraintIsFeasible(
ct);
1035 case ConstraintProto::ConstraintCase::kIntMod:
1036 is_feasible = checker.IntModConstraintIsFeasible(
ct);
1038 case ConstraintProto::ConstraintCase::kIntMin:
1039 is_feasible = checker.IntMinConstraintIsFeasible(
ct);
1041 case ConstraintProto::ConstraintCase::kLinMin:
1042 is_feasible = checker.LinMinConstraintIsFeasible(
ct);
1044 case ConstraintProto::ConstraintCase::kIntMax:
1045 is_feasible = checker.IntMaxConstraintIsFeasible(
ct);
1047 case ConstraintProto::ConstraintCase::kLinMax:
1048 is_feasible = checker.LinMaxConstraintIsFeasible(
ct);
1050 case ConstraintProto::ConstraintCase::kAllDiff:
1051 is_feasible = checker.AllDiffConstraintIsFeasible(
ct);
1053 case ConstraintProto::ConstraintCase::kInterval:
1054 is_feasible = checker.IntervalConstraintIsFeasible(
ct);
1056 case ConstraintProto::ConstraintCase::kNoOverlap:
1057 is_feasible = checker.NoOverlapConstraintIsFeasible(
model,
ct);
1059 case ConstraintProto::ConstraintCase::kNoOverlap2D:
1060 is_feasible = checker.NoOverlap2DConstraintIsFeasible(
model,
ct);
1062 case ConstraintProto::ConstraintCase::kCumulative:
1063 is_feasible = checker.CumulativeConstraintIsFeasible(
model,
ct);
1065 case ConstraintProto::ConstraintCase::kElement:
1066 is_feasible = checker.ElementConstraintIsFeasible(
ct);
1068 case ConstraintProto::ConstraintCase::kTable:
1069 is_feasible = checker.TableConstraintIsFeasible(
ct);
1071 case ConstraintProto::ConstraintCase::kAutomaton:
1072 is_feasible = checker.AutomatonConstraintIsFeasible(
ct);
1074 case ConstraintProto::ConstraintCase::kCircuit:
1075 is_feasible = checker.CircuitConstraintIsFeasible(
ct);
1077 case ConstraintProto::ConstraintCase::kRoutes:
1078 is_feasible = checker.RoutesConstraintIsFeasible(
ct);
1080 case ConstraintProto::ConstraintCase::kCircuitCovering:
1081 is_feasible = checker.CircuitCoveringConstraintIsFeasible(
ct);
1083 case ConstraintProto::ConstraintCase::kInverse:
1084 is_feasible = checker.InverseConstraintIsFeasible(
ct);
1086 case ConstraintProto::ConstraintCase::kReservoir:
1087 is_feasible = checker.ReservoirConstraintIsFeasible(
ct);
1089 case ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET:
1096 VLOG(1) <<
"Failing constraint #" << c <<
" : "
1098 if (mapping_proto !=
nullptr && postsolve_mapping !=
nullptr) {
1099 std::vector<bool> fixed(mapping_proto->variables().size(),
false);
1100 for (
const int var : *postsolve_mapping) fixed[
var] =
true;
1102 VLOG(1) <<
"var: " <<
var <<
" value: " << variable_values[
var]
1103 <<
" was_fixed: " << fixed[
var] <<
" initial_domain: "
1105 <<
" postsolved_domain: "