@@ -105,13 +105,13 @@ std::string ValidateIntegerVariable(const CpModelProto& model, int v) {
105105
106106 // Internally, we often take the negation of a domain, and we also want to
107107 // have sentinel values greater than the min/max of a variable domain, so
108- // the domain must fall in [kint64min + 2, kint64max - 1 ].
108+ // the domain must fall in [-kint64max / 2, kint64max / 2 ].
109109 const int64_t lb = proto.domain (0 );
110110 const int64_t ub = proto.domain (proto.domain_size () - 1 );
111- if (lb < std::numeric_limits<int64_t >::min () + 2 ||
112- ub > std::numeric_limits<int64_t >::max () - 1 ) {
111+ if (lb < - std::numeric_limits<int64_t >::max () / 2 ||
112+ ub > std::numeric_limits<int64_t >::max () / 2 ) {
113113 return absl::StrCat (
114- " var #" , v, " domain do not fall in [kint64min + 2, kint64max - 1 ]. " ,
114+ " var #" , v, " domain do not fall in [-kint64max / 2, kint64max / 2 ]. " ,
115115 ProtobufShortDebugString (proto));
116116 }
117117
@@ -361,8 +361,14 @@ std::string ValidateIntProdConstraint(const CpModelProto& model,
361361 // Detect potential overflow.
362362 Domain product_domain (1 );
363363 for (const LinearExpressionProto& expr : ct.int_prod ().exprs ()) {
364- product_domain = product_domain.ContinuousMultiplicationBy (
365- {MinOfExpression (model, expr), MaxOfExpression (model, expr)});
364+ const int64_t min_expr = MinOfExpression (model, expr);
365+ const int64_t max_expr = MaxOfExpression (model, expr);
366+ if (min_expr == 0 && max_expr == 0 ) {
367+ // An overflow multiplied by zero is still invalid.
368+ continue ;
369+ }
370+ product_domain =
371+ product_domain.ContinuousMultiplicationBy ({min_expr, max_expr});
366372 }
367373
368374 if (product_domain.Max () <= -std ::numeric_limits<int64_t >::max () ||
@@ -918,11 +924,6 @@ std::string ValidateSearchStrategies(const CpModelProto& model) {
918924 " has a domain too large to be used in a"
919925 " SELECT_MEDIAN_VALUE value selection strategy" );
920926 }
921- if (PossibleIntegerOverflow (model, {ref}, {1 })) {
922- // This will become an overflow if translated to an expr.
923- return absl::StrCat (" Possible integer overflow in strategy: " ,
924- ProtobufShortDebugString (strategy));
925- }
926927 }
927928 for (const LinearExpressionProto& expr : strategy.exprs ()) {
928929 for (const int var : expr.vars ()) {
@@ -1025,9 +1026,6 @@ bool PossibleIntegerOverflow(const CpModelProto& model,
10251026}
10261027
10271028std::string ValidateCpModel (const CpModelProto& model, bool after_presolve) {
1028- if (!after_presolve && model.has_symmetry ()) {
1029- return " The symmetry field should be empty and reserved for internal use." ;
1030- }
10311029 int64_t int128_overflow = 0 ;
10321030 for (int v = 0 ; v < model.variables_size (); ++v) {
10331031 RETURN_IF_NOT_EMPTY (ValidateIntegerVariable (model, v));
@@ -1637,26 +1635,51 @@ class ConstraintChecker {
16371635 const int num_arcs = ct.routes ().tails_size ();
16381636 int num_used_arcs = 0 ;
16391637 int num_self_arcs = 0 ;
1638+
1639+ // Compute the number of nodes.
16401640 int num_nodes = 0 ;
1641- std::vector<int > tail_to_head;
1641+ for (int i = 0 ; i < num_arcs; ++i) {
1642+ num_nodes = std::max (num_nodes, 1 + ct.routes ().tails (i));
1643+ num_nodes = std::max (num_nodes, 1 + ct.routes ().heads (i));
1644+ }
1645+
1646+ std::vector<int > tail_to_head (num_nodes, -1 );
1647+ std::vector<bool > has_incoming_arc (num_nodes, false );
1648+ std::vector<int > has_outgoing_arc (num_nodes, false );
16421649 std::vector<int > depot_nexts;
16431650 for (int i = 0 ; i < num_arcs; ++i) {
16441651 const int tail = ct.routes ().tails (i);
16451652 const int head = ct.routes ().heads (i);
1646- num_nodes = std::max (num_nodes, 1 + tail);
1647- num_nodes = std::max (num_nodes, 1 + head);
1648- tail_to_head.resize (num_nodes, -1 );
16491653 if (LiteralIsTrue (ct.routes ().literals (i))) {
1654+ // Check for loops.
1655+ if (tail != 0 ) {
1656+ if (has_outgoing_arc[tail]) {
1657+ VLOG (1 ) << " routes: node " << tail << " has two outgoing arcs" ;
1658+ return false ;
1659+ }
1660+ has_outgoing_arc[tail] = true ;
1661+ }
1662+ if (head != 0 ) {
1663+ if (has_incoming_arc[head]) {
1664+ VLOG (1 ) << " routes: node " << head << " has two incoming arcs" ;
1665+ return false ;
1666+ }
1667+ has_incoming_arc[head] = true ;
1668+ }
1669+
16501670 if (tail == head) {
1651- if (tail == 0 ) return false ;
1671+ if (tail == 0 ) {
1672+ VLOG (1 ) << " Self loop on node 0 are forbidden." ;
1673+ return false ;
1674+ }
16521675 ++num_self_arcs;
16531676 continue ;
16541677 }
16551678 ++num_used_arcs;
16561679 if (tail == 0 ) {
16571680 depot_nexts.push_back (head);
16581681 } else {
1659- if (tail_to_head[tail] != -1 ) return false ;
1682+ DCHECK_EQ (tail_to_head[tail], -1 );
16601683 tail_to_head[tail] = head;
16611684 }
16621685 }
@@ -1769,9 +1792,9 @@ class ConstraintChecker {
17691792 // This indicates that such a constraint was not added to the model.
17701793 // It should probably be a validation error, but it is hard to
17711794 // detect beforehand.
1772- LOG (ERROR ) << " Warning, an interval constraint was likely used "
1773- " without a corresponding linear constraint linking "
1774- " its start, size and end." ;
1795+ VLOG ( 1 ) << " Warning, an interval constraint was likely used "
1796+ " without a corresponding linear constraint linking "
1797+ " its start, size and end." ;
17751798 }
17761799 return false ;
17771800 }
0 commit comments