14"""Methods for building and solving CP-SAT models.
16The following two sections describe the main
17methods for building and solving CP-SAT models.
19* [`CpModel`](#cp_model.CpModel): Methods for creating
20models, including variables and constraints.
21* [`CPSolver`](#cp_model.CpSolver): Methods for solving
22a model and evaluating solutions.
24The following methods implement callbacks that the
25solver calls each time it finds a new solution.
27* [`CpSolverSolutionCallback`](#cp_model.CpSolverSolutionCallback):
28 A general method for implementing callbacks.
29* [`ObjectiveSolutionPrinter`](#cp_model.ObjectiveSolutionPrinter):
30 Print objective values and elapsed time for intermediate solutions.
31* [`VarArraySolutionPrinter`](#cp_model.VarArraySolutionPrinter):
32 Print intermediate solutions (variable values, time).
33* [`VarArrayAndObjectiveSolutionPrinter`]
34 (#cp_model.VarArrayAndObjectiveSolutionPrinter):
35 Print both intermediate solutions and objective values.
37Additional methods for solving CP-SAT models:
39* [`Constraint`](#cp_model.Constraint): A few utility methods for modifying
40 constraints created by `CpModel`.
41* [`LinearExpr`](#lineacp_model.LinearExpr): Methods for creating constraints
42 and the objective from large arrays of coefficients.
44Other methods and functions listed are primarily used for developing OR-Tools,
45rather than for solving specific optimization problems.
77Domain = sorted_interval_list.Domain
89UNKNOWN = cp_model_pb2.UNKNOWN
90MODEL_INVALID = cp_model_pb2.MODEL_INVALID
91FEASIBLE = cp_model_pb2.FEASIBLE
92INFEASIBLE = cp_model_pb2.INFEASIBLE
93OPTIMAL = cp_model_pb2.OPTIMAL
96CHOOSE_FIRST = cp_model_pb2.DecisionStrategyProto.CHOOSE_FIRST
97CHOOSE_LOWEST_MIN = cp_model_pb2.DecisionStrategyProto.CHOOSE_LOWEST_MIN
98CHOOSE_HIGHEST_MAX = cp_model_pb2.DecisionStrategyProto.CHOOSE_HIGHEST_MAX
99CHOOSE_MIN_DOMAIN_SIZE = cp_model_pb2.DecisionStrategyProto.CHOOSE_MIN_DOMAIN_SIZE
100CHOOSE_MAX_DOMAIN_SIZE = cp_model_pb2.DecisionStrategyProto.CHOOSE_MAX_DOMAIN_SIZE
103SELECT_MIN_VALUE = cp_model_pb2.DecisionStrategyProto.SELECT_MIN_VALUE
104SELECT_MAX_VALUE = cp_model_pb2.DecisionStrategyProto.SELECT_MAX_VALUE
105SELECT_LOWER_HALF = cp_model_pb2.DecisionStrategyProto.SELECT_LOWER_HALF
106SELECT_UPPER_HALF = cp_model_pb2.DecisionStrategyProto.SELECT_UPPER_HALF
109AUTOMATIC_SEARCH = sat_parameters_pb2.SatParameters.AUTOMATIC_SEARCH
110FIXED_SEARCH = sat_parameters_pb2.SatParameters.FIXED_SEARCH
111PORTFOLIO_SEARCH = sat_parameters_pb2.SatParameters.PORTFOLIO_SEARCH
112LP_SEARCH = sat_parameters_pb2.SatParameters.LP_SEARCH
113PSEUDO_COST_SEARCH = sat_parameters_pb2.SatParameters.PSEUDO_COST_SEARCH
114PORTFOLIO_WITH_QUICK_RESTART_SEARCH = (
115 sat_parameters_pb2.SatParameters.PORTFOLIO_WITH_QUICK_RESTART_SEARCH
117HINT_SEARCH = sat_parameters_pb2.SatParameters.HINT_SEARCH
118PARTIAL_FIXED_SEARCH = sat_parameters_pb2.SatParameters.PARTIAL_FIXED_SEARCH
119RANDOMIZED_SEARCH = sat_parameters_pb2.SatParameters.RANDOMIZED_SEARCH
122IntegralT = Union[int, np.int8, np.uint8, np.int32, np.uint32, np.int64, np.uint64]
155LiteralT = Union[
"IntVar",
"_NotBooleanVariable", IntegralT, bool]
156BoolVarT = Union[
"IntVar",
"_NotBooleanVariable"]
157VariableT = Union[
"IntVar", IntegralT]
160LinearExprT = Union[
"LinearExpr",
"IntVar", IntegralT]
161ObjLinearExprT = Union[
"LinearExpr", NumberT]
162BoundedLinearExprT = Union[
"BoundedLinearExpression", bool]
164ArcT = Tuple[IntegralT, IntegralT, LiteralT]
165_IndexOrSeries = Union[pd.Index, pd.Series]
169 """Displays a flattened list of intervals."""
171 for i
in range(0, len(bounds), 2):
174 if bounds[i] == bounds[i + 1]:
175 out += str(bounds[i])
177 out += str(bounds[i]) +
".." + str(bounds[i + 1])
181def short_name(model: cp_model_pb2.CpModelProto, i: int) -> str:
182 """Returns a short name of an integer variable, or its negation."""
185 v = model.variables[i]
188 elif len(v.domain) == 2
and v.domain[0] == v.domain[1]:
189 return str(v.domain[0])
195 model: cp_model_pb2.CpModelProto, e: cp_model_pb2.LinearExpressionProto
197 """Pretty-print LinearExpressionProto instances."""
207 result = f
"-{var_name}"
209 result = f
"{coeff} * {var_name}"
211 result = f
"{result} + {e.offset}"
213 result = f
"{result} - {-e.offset}"
220 """Holds an integer linear expression.
222 A linear expression is built from integer constants and variables.
223 For example, `x + 2 * (y - z + 1)`.
225 Linear expressions are used in CP-SAT models in constraints and in the
228 * You can define linear constraints as in:
231 model.add(x + 2 * y <= 5)
232 model.add(sum(array_of_vars) == 5)
235 * In CP-SAT, the objective is a linear expression:
238 model.minimize(x + 2 * y + z)
241 * For large arrays, using the LinearExpr class is faster that using the python
242 `sum()` function. You can create constraints and the objective from lists of
243 linear expressions or coefficients as follows:
246 model.minimize(cp_model.LinearExpr.sum(expressions))
247 model.add(cp_model.LinearExpr.weighted_sum(expressions, coefficients) >= 0)
252 def sum(cls, expressions: Sequence[LinearExprT]) -> LinearExprT:
253 """Creates the expression sum(expressions)."""
254 if len(expressions) == 1:
255 return expressions[0]
262 expressions: Sequence[LinearExprT],
263 coefficients: Sequence[IntegralT],
264 ) -> LinearExprT: ...
270 expressions: Sequence[ObjLinearExprT],
271 coefficients: Sequence[NumberT],
272 ) -> ObjLinearExprT: ...
276 """Creates the expression sum(expressions[i] * coefficients[i])."""
277 if LinearExpr.is_empty_or_all_null(coefficients):
279 elif len(expressions) == 1:
280 return expressions[0] * coefficients[0]
288 expressions: LinearExprT,
289 coefficients: IntegralT,
290 ) -> LinearExprT: ...
296 expressions: ObjLinearExprT,
297 coefficients: NumberT,
298 ) -> ObjLinearExprT: ...
301 def term(cls, expression, coefficient):
302 """Creates `expression * coefficient`."""
303 if cmh.is_zero(coefficient):
306 return expression * coefficient
310 for c
in coefficients:
311 if not cmh.is_zero(c):
318 model: cp_model_pb2.CpModelProto,
319 proto: cp_model_pb2.LinearExpressionProto,
321 """Recreate a LinearExpr from a LinearExpressionProto."""
322 offset = proto.offset
323 num_elements = len(proto.vars)
324 if num_elements == 0:
326 elif num_elements == 1:
328 IntVar(model, proto.vars[0],
None) * proto.coeffs[0] + offset
334 for index, coeff
in zip(proto.vars, proto.coeffs):
335 variables.append(
IntVar(model, index,
None))
337 if not cmh.is_one(coeff):
345 """Scans the expression, and returns (var_coef_map, constant)."""
346 coeffs: Dict[
"IntVar", int] = collections.defaultdict(int)
348 to_process: List[Tuple[LinearExprT, int]] = [(self, 1)]
352 expr, coeff = to_process.pop()
353 if isinstance(expr, IntegralTypes):
354 constant += coeff * int(expr)
355 elif isinstance(expr, _ProductCst):
356 to_process.append((expr.expression(), coeff * expr.coefficient()))
357 elif isinstance(expr, _Sum):
358 to_process.append((expr.left(), coeff))
359 to_process.append((expr.right(), coeff))
360 elif isinstance(expr, _SumArray):
361 for e
in expr.expressions():
362 to_process.append((e, coeff))
363 constant += expr.constant() * coeff
364 elif isinstance(expr, _WeightedSum):
365 for e, c
in zip(expr.expressions(), expr.coefficients()):
366 to_process.append((e, coeff * c))
367 constant += expr.constant() * coeff
368 elif isinstance(expr, IntVar):
369 coeffs[expr] += coeff
370 elif isinstance(expr, _NotBooleanVariable):
372 coeffs[expr.negated()] -= coeff
373 elif isinstance(expr, NumberTypes):
375 f
"Floating point constants are not supported in constraints: {expr}"
378 raise TypeError(
"Unrecognized linear expression: " + str(expr))
380 return coeffs, constant
384 ) -> Tuple[Dict["IntVar", float], float, bool]:
385 """Scans the expression. Returns (var_coef_map, constant, is_integer)."""
386 coeffs: Dict[
"IntVar", Union[int, float]] = {}
387 constant: Union[int, float] = 0
388 to_process: List[Tuple[LinearExprT, Union[int, float]]] = [(self, 1)]
390 expr, coeff = to_process.pop()
391 if isinstance(expr, IntegralTypes):
392 constant += coeff * int(expr)
393 elif isinstance(expr, NumberTypes):
394 constant += coeff * float(expr)
395 elif isinstance(expr, _ProductCst):
396 to_process.append((expr.expression(), coeff * expr.coefficient()))
397 elif isinstance(expr, _Sum):
398 to_process.append((expr.left(), coeff))
399 to_process.append((expr.right(), coeff))
400 elif isinstance(expr, _SumArray):
401 for e
in expr.expressions():
402 to_process.append((e, coeff))
403 constant += expr.constant() * coeff
404 elif isinstance(expr, _WeightedSum):
405 for e, c
in zip(expr.expressions(), expr.coefficients()):
406 to_process.append((e, coeff * c))
407 constant += expr.constant() * coeff
408 elif isinstance(expr, IntVar):
410 coeffs[expr] += coeff
413 elif isinstance(expr, _NotBooleanVariable):
415 if expr.negated()
in coeffs:
416 coeffs[expr.negated()] -= coeff
418 coeffs[expr.negated()] = -coeff
420 raise TypeError(
"Unrecognized linear expression: " + str(expr))
421 is_integer = isinstance(constant, IntegralTypes)
423 for coeff
in coeffs.values():
424 if not isinstance(coeff, IntegralTypes):
427 return coeffs, constant, is_integer
430 return object.__hash__(self)
433 raise NotImplementedError(
434 "calling abs() on a linear expression is not supported, "
435 "please use CpModel.add_abs_equality"
439 def __add__(self, arg:
"LinearExpr") ->
"LinearExpr": ...
442 def __add__(self, arg: NumberT) ->
"LinearExpr": ...
447 return _Sum(self, arg)
450 def __radd__(self, arg:
"LinearExpr") ->
"LinearExpr": ...
453 def __radd__(self, arg: NumberT) ->
"LinearExpr": ...
459 def __sub__(self, arg:
"LinearExpr") ->
"LinearExpr": ...
462 def __sub__(self, arg: NumberT) ->
"LinearExpr": ...
467 if isinstance(arg, NumberTypes):
468 arg = cmh.assert_is_a_number(arg)
469 return _Sum(self, -arg)
471 return _Sum(self, -arg)
474 def __rsub__(self, arg:
"LinearExpr") ->
"LinearExpr": ...
477 def __rsub__(self, arg: NumberT) ->
"LinearExpr": ...
480 return _Sum(-self, arg)
483 def __mul__(self, arg: IntegralT) -> Union[
"LinearExpr", IntegralT]: ...
486 def __mul__(self, arg: NumberT) -> Union[
"LinearExpr", NumberT]: ...
489 arg = cmh.assert_is_a_number(arg)
492 elif cmh.is_zero(arg):
497 def __rmul__(self, arg: IntegralT) -> Union[
"LinearExpr", IntegralT]: ...
500 def __rmul__(self, arg: NumberT) -> Union[
"LinearExpr", NumberT]: ...
506 raise NotImplementedError(
507 "calling / on a linear expression is not supported, "
508 "please use CpModel.add_division_equality"
512 raise NotImplementedError(
513 "calling // on a linear expression is not supported, "
514 "please use CpModel.add_division_equality"
518 raise NotImplementedError(
519 "calling %% on a linear expression is not supported, "
520 "please use CpModel.add_modulo_equality"
524 raise NotImplementedError(
525 "calling ** on a linear expression is not supported, "
526 "please use CpModel.add_multiplication_equality"
530 raise NotImplementedError(
531 "calling left shift on a linear expression is not supported"
535 raise NotImplementedError(
536 "calling right shift on a linear expression is not supported"
540 raise NotImplementedError(
541 "calling and on a linear expression is not supported, "
542 "please use CpModel.add_bool_and"
546 raise NotImplementedError(
547 "calling or on a linear expression is not supported, "
548 "please use CpModel.add_bool_or"
552 raise NotImplementedError(
553 "calling xor on a linear expression is not supported, "
554 "please use CpModel.add_bool_xor"
561 raise NotImplementedError(
562 "Evaluating a LinearExpr instance as a Boolean is not implemented."
565 def __eq__(self, arg: LinearExprT) -> BoundedLinearExprT:
568 if isinstance(arg, IntegralTypes):
569 arg = cmh.assert_is_int64(arg)
571 elif isinstance(arg, LinearExpr):
576 def __ge__(self, arg: LinearExprT) ->
"BoundedLinearExpression":
577 if isinstance(arg, IntegralTypes):
578 arg = cmh.assert_is_int64(arg)
583 def __le__(self, arg: LinearExprT) ->
"BoundedLinearExpression":
584 if isinstance(arg, IntegralTypes):
585 arg = cmh.assert_is_int64(arg)
590 def __lt__(self, arg: LinearExprT) ->
"BoundedLinearExpression":
591 if isinstance(arg, IntegralTypes):
592 arg = cmh.assert_is_int64(arg)
594 raise ArithmeticError(
"< INT_MIN is not supported")
599 def __gt__(self, arg: LinearExprT) ->
"BoundedLinearExpression":
600 if isinstance(arg, IntegralTypes):
601 arg = cmh.assert_is_int64(arg)
603 raise ArithmeticError(
"> INT_MAX is not supported")
608 def __ne__(self, arg: LinearExprT) -> BoundedLinearExprT:
611 if isinstance(arg, IntegralTypes):
612 arg = cmh.assert_is_int64(arg)
619 self, [INT_MIN, arg - 1, arg + 1, INT_MAX]
621 elif isinstance(arg, LinearExpr):
629 def Sum(cls, expressions: Sequence[LinearExprT]) -> LinearExprT:
630 """Creates the expression sum(expressions)."""
631 return cls.
sum(expressions)
637 expressions: Sequence[LinearExprT],
638 coefficients: Sequence[IntegralT],
639 ) -> LinearExprT: ...
645 expressions: Sequence[ObjLinearExprT],
646 coefficients: Sequence[NumberT],
647 ) -> ObjLinearExprT: ...
651 """Creates the expression sum(expressions[i] * coefficients[i])."""
658 expressions: LinearExprT,
659 coefficients: IntegralT,
660 ) -> LinearExprT: ...
666 expressions: ObjLinearExprT,
667 coefficients: NumberT,
668 ) -> ObjLinearExprT: ...
671 def Term(cls, expression, coefficient):
672 """Creates `expression * coefficient`."""
679 """Represents the sum of two LinearExprs."""
682 for x
in [left, right]:
683 if not isinstance(x, (NumberTypes, LinearExpr)):
684 raise TypeError(
"not an linear expression: " + str(x))
695 return f
"({self.__left} + {self.__right})"
698 return f
"sum({self.__left!r}, {self.__right!r})"
702 """Represents the product of a LinearExpr by a constant."""
705 coeff = cmh.assert_is_a_number(coeff)
706 if isinstance(expr, _ProductCst):
707 self.
__expr = expr.expression()
708 self.
__coef = expr.coefficient() * coeff
715 return "-" + str(self.
__expr)
717 return "(" + str(self.
__coef) +
" * " + str(self.
__expr) +
")"
720 return f
"ProductCst({self.__expr!r}, {self.__coef!r})"
730 """Represents the sum of a list of LinearExpr and a constant."""
732 def __init__(self, expressions, constant=0) -> None:
735 for x
in expressions:
736 if isinstance(x, NumberTypes):
739 x = cmh.assert_is_a_number(x)
741 elif isinstance(x, LinearExpr):
744 raise TypeError(
"not an linear expression: " + str(x))
748 exprs_str =
" + ".join(
749 map(repr, itertools.chain(self.
__expressions, constant_terms))
753 return f
"({exprs_str})"
757 return f
"SumArray({exprs_str}, {self.__constant})"
767 """Represents sum(ai * xi) + b."""
769 def __init__(self, expressions, coefficients, constant=0) -> None:
773 if len(expressions) != len(coefficients):
775 "In the LinearExpr.weighted_sum method, the expression array and the "
776 " coefficient array must have the same length."
778 for e, c
in zip(expressions, coefficients):
779 c = cmh.assert_is_a_number(c)
782 if isinstance(e, NumberTypes):
783 e = cmh.assert_is_a_number(e)
785 elif isinstance(e, LinearExpr):
789 raise TypeError(
"not an linear expression: " + str(e))
794 if not output
and cmh.is_one(coeff):
796 elif not output
and cmh.is_minus_one(coeff):
797 output =
"-" + str(expr)
799 output = f
"{coeff} * {expr}"
800 elif cmh.is_one(coeff):
801 output += f
" + {expr}"
802 elif cmh.is_minus_one(coeff):
803 output += f
" - {expr}"
805 output += f
" + {coeff} * {expr}"
807 output += f
" - {-coeff} * {expr}"
811 output += f
" + {self.__constant}"
813 output += f
" - {-self.__constant}"
818 f
"weighted_sum({self.__expressions!r}, {self.__coefficients!r},"
819 f
" {self.__constant})"
833 """An integer variable.
835 An IntVar is an object that can take on any integer value within defined
836 ranges. Variables appear in constraint like:
839 AllDifferent([x, y, z])
841 Solving a model is equivalent to finding, for each variable, a single value
842 from the set of initial values (called the initial domain), such that the
843 model is feasible, or optimal if you provided an objective function.
848 model: cp_model_pb2.CpModelProto,
849 domain: Union[int, sorted_interval_list.Domain],
852 """See CpModel.new_int_var below."""
854 self.
__var__var: cp_model_pb2.IntegerVariableProto
855 self.
__negation: Optional[_NotBooleanVariable] =
None
863 if isinstance(domain, IntegralTypes)
and name
is None:
867 self.
__index = len(model.variables)
870 cast(sorted_interval_list.Domain, domain).flattened_intervals()
877 """Returns the index of the variable in the model."""
881 def proto(self) -> cp_model_pb2.IntegerVariableProto:
882 """Returns the variable protobuf."""
886 """Returns true if self == other in the python sense."""
887 if not isinstance(other, IntVar):
900 return "unnamed_var_%i" % self.
__index
913 """Returns the negation of a Boolean variable.
915 This method implements the logical negation of a Boolean variable.
916 It is only valid if the variable has a Boolean domain (0 or 1).
918 Note that this method is nilpotent: `x.negated().negated() == x`.
922 if bound < 0
or bound > 1:
924 f
"cannot call negated on a non boolean variable: {self}"
931 """Returns the logical negation of a Boolean variable."""
941 def Proto(self) -> cp_model_pb2.IntegerVariableProto:
951 """Negation of a boolean variable."""
964 """Returns the logical negation of a Boolean literal."""
975 raise NotImplementedError(
976 "Evaluating a literal as a Boolean value is not implemented."
981 def Not(self) -> "IntVar":
991 """Represents a linear constraint: `lb <= linear expression <= ub`.
993 The only use of this class is to be added to the CpModel through
994 `CpModel.add(expression)`, as in:
996 model.add(x + 2 * y -1 >= z)
999 def __init__(self, expr: LinearExprT, bounds: Sequence[int]) ->
None:
1000 self.
__expr: LinearExprT = expr
1006 if lb > INT_MIN
and ub < INT_MAX:
1008 return str(self.
__expr) +
" == " + str(lb)
1010 return str(lb) +
" <= " + str(self.
__expr) +
" <= " + str(ub)
1012 return str(self.
__expr) +
" >= " + str(lb)
1014 return str(self.
__expr) +
" <= " + str(ub)
1016 return "True (unbounded expr " + str(self.
__expr) +
")"
1035 if isinstance(expr, LinearExpr):
1036 coeffs_map, constant = expr.get_integer_var_value_map()
1037 all_coeffs = set(coeffs_map.values())
1040 different_vars = set([-1, 1])
1041 ne_bounds = [INT_MIN, -1, 1, INT_MAX]
1043 len(coeffs_map) == 1
1044 and all_coeffs == same_var
1050 len(coeffs_map) == 2
1051 and all_coeffs == different_vars
1057 raise NotImplementedError(
1058 f
'Evaluating a BoundedLinearExpression "{self}" as a Boolean value'
1059 +
" is not supported."
1064 """Base class for constraints.
1066 Constraints are built by the CpModel through the add<XXX> methods.
1067 Once created by the CpModel class, they are automatically added to the model.
1068 The purpose of this class is to allow specification of enforcement literals
1069 for this constraint.
1071 b = model.new_bool_var('b')
1072 x = model.new_int_var(0, 10, 'x')
1073 y = model.new_int_var(0, 10, 'y')
1075 model.add(x + 2 * y == 5).only_enforce_if(b.negated())
1080 cp_model:
"CpModel",
1082 self.
__index: int = len(cp_model.proto.constraints)
1085 cp_model.proto.constraints.add()
1095 """Adds an enforcement literal to the constraint.
1097 This method adds one or more literals (that is, a boolean variable or its
1098 negation) as enforcement literals. The conjunction of all these literals
1099 determines whether the constraint is active or not. It acts as an
1100 implication, so if the conjunction is true, it implies that the constraint
1101 must be enforced. If it is false, then the constraint is ignored.
1103 BoolOr, BoolAnd, and linear constraints all support enforcement literals.
1106 *boolvar: One or more Boolean literals.
1112 if (cmh.is_boolean(lit)
and lit)
or (
1113 isinstance(lit, IntegralTypes)
and lit == 1
1117 elif (cmh.is_boolean(lit)
and not lit)
or (
1118 isinstance(lit, IntegralTypes)
and lit == 0
1125 cast(Union[IntVar, _NotBooleanVariable], lit).index
1130 """Sets the name of the constraint."""
1139 """Returns the name of the constraint."""
1146 """Returns the index of the constraint in the model."""
1150 def proto(self) -> cp_model_pb2.ConstraintProto:
1151 """Returns the constraint protobuf."""
1156 OnlyEnforceIf = only_enforce_if
1157 WithName = with_name
1165 def Proto(self) -> cp_model_pb2.ConstraintProto:
1172 """Represents an Interval variable.
1174 An interval variable is both a constraint and a variable. It is defined by
1175 three integer variables: start, size, and end.
1177 It is a constraint because, internally, it enforces that start + size == end.
1179 It is also a variable as it can appear in specific scheduling constraints:
1180 NoOverlap, NoOverlap2D, Cumulative.
1182 Optionally, an enforcement literal can be added to this constraint, in which
1183 case these scheduling constraints will ignore interval variables with
1184 enforcement literals assigned to false. Conversely, these constraints will
1185 also set these enforcement literals to false if they cannot fit these
1186 intervals into the schedule.
1189 ValueError: if start, size, end are not defined, or have the wrong type.
1194 model: cp_model_pb2.CpModelProto,
1195 start: Union[cp_model_pb2.LinearExpressionProto, int],
1196 size: Optional[cp_model_pb2.LinearExpressionProto],
1197 end: Optional[cp_model_pb2.LinearExpressionProto],
1198 is_present_index: Optional[int],
1199 name: Optional[str],
1201 self.
__model: cp_model_pb2.CpModelProto = model
1203 self.
__ct__ct: cp_model_pb2.ConstraintProto
1211 if isinstance(start, int):
1212 if size
is not None:
1213 raise ValueError(
"size should be None")
1215 raise ValueError(
"end should be None")
1216 if is_present_index
is not None:
1217 raise ValueError(
"is_present_index should be None")
1218 self.
__index = cast(int, start)
1221 self.
__index = len(model.constraints)
1224 raise TypeError(
"start is not defined")
1225 self.
__ct__ct.interval.start.CopyFrom(start)
1227 raise TypeError(
"size is not defined")
1228 self.
__ct__ct.interval.size.CopyFrom(size)
1230 raise TypeError(
"end is not defined")
1231 self.
__ct__ct.interval.end.CopyFrom(end)
1232 if is_present_index
is not None:
1233 self.
__ct__ct.enforcement_literal.append(is_present_index)
1239 """Returns the index of the interval constraint in the model."""
1243 def proto(self) -> cp_model_pb2.IntervalConstraintProto:
1244 """Returns the interval protobuf."""
1252 if self.
__ct__ct.enforcement_literal:
1253 return "%s(start = %s, size = %s, end = %s, is_present = %s)" % (
1261 return "%s(start = %s, size = %s, end = %s)" % (
1275 return LinearExpr.rebuild_from_linear_expression_proto(
1280 return LinearExpr.rebuild_from_linear_expression_proto(
1285 return LinearExpr.rebuild_from_linear_expression_proto(
1297 def Proto(self) -> cp_model_pb2.IntervalConstraintProto:
1300 StartExpr = start_expr
1301 SizeExpr = size_expr
1308 """Checks if literal is either True, or a Boolean literals fixed to True."""
1309 if isinstance(literal, IntVar):
1310 proto = literal.proto
1311 return len(proto.domain) == 2
and proto.domain[0] == 1
and proto.domain[1] == 1
1312 if isinstance(literal, _NotBooleanVariable):
1313 proto = literal.negated().proto
1314 return len(proto.domain) == 2
and proto.domain[0] == 0
and proto.domain[1] == 0
1315 if isinstance(literal, IntegralTypes):
1316 return int(literal) == 1
1321 """Checks if literal is either False, or a Boolean literals fixed to False."""
1322 if isinstance(literal, IntVar):
1323 proto = literal.proto
1324 return len(proto.domain) == 2
and proto.domain[0] == 0
and proto.domain[1] == 0
1325 if isinstance(literal, _NotBooleanVariable):
1326 proto = literal.negated().proto
1327 return len(proto.domain) == 2
and proto.domain[0] == 1
and proto.domain[1] == 1
1328 if isinstance(literal, IntegralTypes):
1329 return int(literal) == 0
1334 """Methods for building a CP model.
1336 Methods beginning with:
1338 * ```New``` create integer, boolean, or interval variables.
1339 * ```add``` create new constraints and add them to the model.
1343 self.
__model: cp_model_pb2.CpModelProto = cp_model_pb2.CpModelProto()
1349 """Returns the name of the model."""
1356 """Sets the name of the model."""
1361 def new_int_var(self, lb: IntegralT, ub: IntegralT, name: str) -> IntVar:
1362 """Create an integer variable with domain [lb, ub].
1364 The CP-SAT solver is limited to integer variables. If you have fractional
1365 values, scale them up so that they become integers; if you have strings,
1366 encode them as integers.
1369 lb: Lower bound for the variable.
1370 ub: Upper bound for the variable.
1371 name: The name of the variable.
1374 a variable whose domain is [lb, ub].
1377 return IntVar(self.
__model, sorted_interval_list.Domain(lb, ub), name)
1380 self, domain: sorted_interval_list.Domain, name: str
1382 """Create an integer variable from a domain.
1384 A domain is a set of integers specified by a collection of intervals.
1385 For example, `model.new_int_var_from_domain(cp_model.
1386 Domain.from_intervals([[1, 2], [4, 6]]), 'x')`
1389 domain: An instance of the Domain class.
1390 name: The name of the variable.
1393 a variable whose domain is the given domain.
1398 """Creates a 0-1 variable with the given name."""
1399 return IntVar(self.
__model, sorted_interval_list.Domain(0, 1), name)
1402 """Declares a constant integer."""
1409 lower_bounds: Union[IntegralT, pd.Series],
1410 upper_bounds: Union[IntegralT, pd.Series],
1412 """Creates a series of (scalar-valued) variables with the given name.
1415 name (str): Required. The name of the variable set.
1416 index (pd.Index): Required. The index to use for the variable set.
1417 lower_bounds (Union[int, pd.Series]): A lower bound for variables in the
1418 set. If a `pd.Series` is passed in, it will be based on the
1419 corresponding values of the pd.Series.
1420 upper_bounds (Union[int, pd.Series]): An upper bound for variables in the
1421 set. If a `pd.Series` is passed in, it will be based on the
1422 corresponding values of the pd.Series.
1425 pd.Series: The variable set indexed by its corresponding dimensions.
1428 TypeError: if the `index` is invalid (e.g. a `DataFrame`).
1429 ValueError: if the `name` is not a valid identifier or already exists.
1430 ValueError: if the `lowerbound` is greater than the `upperbound`.
1431 ValueError: if the index of `lower_bound`, or `upper_bound` does not match
1434 if not isinstance(index, pd.Index):
1435 raise TypeError(
"Non-index object is used as index")
1436 if not name.isidentifier():
1437 raise ValueError(
"name={} is not a valid identifier".format(name))
1439 isinstance(lower_bounds, IntegralTypes)
1440 and isinstance(upper_bounds, IntegralTypes)
1441 and lower_bounds > upper_bounds
1444 f
"lower_bound={lower_bounds} is greater than"
1445 f
" upper_bound={upper_bounds} for variable set={name}"
1460 name=f
"{name}[{i}]",
1461 domain=sorted_interval_list.Domain(
1462 lower_bounds[i], upper_bounds[i]
1474 """Creates a series of (scalar-valued) variables with the given name.
1477 name (str): Required. The name of the variable set.
1478 index (pd.Index): Required. The index to use for the variable set.
1481 pd.Series: The variable set indexed by its corresponding dimensions.
1484 TypeError: if the `index` is invalid (e.g. a `DataFrame`).
1485 ValueError: if the `name` is not a valid identifier or already exists.
1488 name=name, index=index, lower_bounds=0, upper_bounds=1
1494 self, linear_expr: LinearExprT, lb: IntegralT, ub: IntegralT
1496 """Adds the constraint: `lb <= linear_expr <= ub`."""
1498 linear_expr, sorted_interval_list.Domain(lb, ub)
1502 self, linear_expr: LinearExprT, domain: sorted_interval_list.Domain
1504 """Adds the constraint: `linear_expr` in `domain`."""
1505 if isinstance(linear_expr, LinearExpr):
1507 model_ct = self.
__model.constraints[ct.index]
1508 coeffs_map, constant = linear_expr.get_integer_var_value_map()
1509 for t
in coeffs_map.items():
1510 if not isinstance(t[0], IntVar):
1511 raise TypeError(
"Wrong argument" + str(t))
1512 c = cmh.assert_is_int64(t[1])
1513 model_ct.linear.vars.append(t[0].index)
1514 model_ct.linear.coeffs.append(c)
1515 model_ct.linear.domain.extend(
1517 cmh.capped_subtraction(x, constant)
1518 for x
in domain.flattened_intervals()
1522 if isinstance(linear_expr, IntegralTypes):
1523 if not domain.contains(int(linear_expr)):
1528 "not supported: CpModel.add_linear_expression_in_domain("
1536 def add(self, ct: BoundedLinearExpression) -> Constraint: ...
1539 def add(self, ct: Union[bool, np.bool_]) -> Constraint: ...
1542 """Adds a `BoundedLinearExpression` to the model.
1545 ct: A [`BoundedLinearExpression`](#boundedlinearexpression).
1548 An instance of the `Constraint` class.
1550 if isinstance(ct, BoundedLinearExpression):
1553 sorted_interval_list.Domain.from_flat_intervals(ct.bounds()),
1555 if ct
and cmh.is_boolean(ct):
1557 if not ct
and cmh.is_boolean(ct):
1559 raise TypeError(
"not supported: CpModel.add(" + str(ct) +
")")
1570 """Adds AllDifferent(expressions).
1572 This constraint forces all expressions to have different values.
1575 *expressions: simple expressions of the form a * var + constant.
1578 An instance of the `Constraint` class.
1581 model_ct = self.
__model.constraints[ct.index]
1583 model_ct.all_diff.exprs.extend(
1589 self, index: VariableT, variables: Sequence[VariableT], target: VariableT
1591 """Adds the element constraint: `variables[index] == target`.
1594 index: The index of the variable that's being constrained.
1595 variables: A list of variables.
1596 target: The value that the variable must be equal to.
1599 An instance of the `Constraint` class.
1603 raise ValueError(
"add_element expects a non-empty variables array")
1605 if isinstance(index, IntegralTypes):
1606 variable: VariableT = list(variables)[int(index)]
1607 return self.
addaddadd(variable == target)
1610 model_ct = self.
__model.constraints[ct.index]
1617 """Adds Circuit(arcs).
1619 Adds a circuit constraint from a sparse list of arcs that encode the graph.
1621 A circuit is a unique Hamiltonian path in a subgraph of the total
1622 graph. In case a node 'i' is not in the path, then there must be a
1623 loop arc 'i -> i' associated with a true literal. Otherwise
1624 this constraint will fail.
1627 arcs: a list of arcs. An arc is a tuple (source_node, destination_node,
1628 literal). The arc is selected in the circuit if the literal is true.
1629 Both source_node and destination_node must be integers between 0 and the
1630 number of nodes - 1.
1633 An instance of the `Constraint` class.
1636 ValueError: If the list of arcs is empty.
1639 raise ValueError(
"add_circuit expects a non-empty array of arcs")
1641 model_ct = self.
__model.constraints[ct.index]
1643 tail = cmh.assert_is_int32(arc[0])
1644 head = cmh.assert_is_int32(arc[1])
1646 model_ct.circuit.tails.append(tail)
1647 model_ct.circuit.heads.append(head)
1648 model_ct.circuit.literals.append(lit)
1652 """Adds a multiple circuit constraint, aka the 'VRP' constraint.
1654 The direct graph where arc #i (from tails[i] to head[i]) is present iff
1655 literals[i] is true must satisfy this set of properties:
1656 - #incoming arcs == 1 except for node 0.
1657 - #outgoing arcs == 1 except for node 0.
1658 - for node zero, #incoming arcs == #outgoing arcs.
1659 - There are no duplicate arcs.
1660 - Self-arcs are allowed except for node 0.
1661 - There is no cycle in this graph, except through node 0.
1664 arcs: a list of arcs. An arc is a tuple (source_node, destination_node,
1665 literal). The arc is selected in the circuit if the literal is true.
1666 Both source_node and destination_node must be integers between 0 and the
1667 number of nodes - 1.
1670 An instance of the `Constraint` class.
1673 ValueError: If the list of arcs is empty.
1676 raise ValueError(
"add_multiple_circuit expects a non-empty array of arcs")
1678 model_ct = self.
__model.constraints[ct.index]
1680 tail = cmh.assert_is_int32(arc[0])
1681 head = cmh.assert_is_int32(arc[1])
1683 model_ct.routes.tails.append(tail)
1684 model_ct.routes.heads.append(head)
1685 model_ct.routes.literals.append(lit)
1690 variables: Sequence[VariableT],
1691 tuples_list: Iterable[Sequence[IntegralT]],
1693 """Adds AllowedAssignments(variables, tuples_list).
1695 An AllowedAssignments constraint is a constraint on an array of variables,
1696 which requires that when all variables are assigned values, the resulting
1697 array equals one of the tuples in `tuple_list`.
1700 variables: A list of variables.
1701 tuples_list: A list of admissible tuples. Each tuple must have the same
1702 length as the variables, and the ith value of a tuple corresponds to the
1706 An instance of the `Constraint` class.
1709 TypeError: If a tuple does not have the same size as the list of
1711 ValueError: If the array of variables is empty.
1716 "add_allowed_assignments expects a non-empty variables array"
1720 model_ct = self.
__model.constraints[ct.index]
1722 arity: int = len(variables)
1723 for t
in tuples_list:
1725 raise TypeError(
"Tuple " + str(t) +
" has the wrong arity")
1729 model_ct.table.values.extend(a
for b
in tuples_list
for a
in b)
1730 except ValueError
as ex:
1731 raise TypeError(f
"add_xxx_assignment: Not an integer or does not fit in an int64_t: {ex.args}")
from ex
1737 variables: Sequence[VariableT],
1738 tuples_list: Iterable[Sequence[IntegralT]],
1740 """Adds add_forbidden_assignments(variables, [tuples_list]).
1742 A ForbiddenAssignments constraint is a constraint on an array of variables
1743 where the list of impossible combinations is provided in the tuples list.
1746 variables: A list of variables.
1747 tuples_list: A list of forbidden tuples. Each tuple must have the same
1748 length as the variables, and the *i*th value of a tuple corresponds to
1752 An instance of the `Constraint` class.
1755 TypeError: If a tuple does not have the same size as the list of
1757 ValueError: If the array of variables is empty.
1762 "add_forbidden_assignments expects a non-empty variables array"
1765 index = len(self.
__model.constraints)
1767 self.
__model.constraints[index].table.negated =
True
1772 transition_variables: Sequence[VariableT],
1773 starting_state: IntegralT,
1774 final_states: Sequence[IntegralT],
1775 transition_triples: Sequence[Tuple[IntegralT, IntegralT, IntegralT]],
1777 """Adds an automaton constraint.
1779 An automaton constraint takes a list of variables (of size *n*), an initial
1780 state, a set of final states, and a set of transitions. A transition is a
1781 triplet (*tail*, *transition*, *head*), where *tail* and *head* are states,
1782 and *transition* is the label of an arc from *head* to *tail*,
1783 corresponding to the value of one variable in the list of variables.
1785 This automaton will be unrolled into a flow with *n* + 1 phases. Each phase
1786 contains the possible states of the automaton. The first state contains the
1787 initial state. The last phase contains the final states.
1789 Between two consecutive phases *i* and *i* + 1, the automaton creates a set
1790 of arcs. For each transition (*tail*, *transition*, *head*), it will add
1791 an arc from the state *tail* of phase *i* and the state *head* of phase
1792 *i* + 1. This arc is labeled by the value *transition* of the variables
1793 `variables[i]`. That is, this arc can only be selected if `variables[i]`
1794 is assigned the value *transition*.
1796 A feasible solution of this constraint is an assignment of variables such
1797 that, starting from the initial state in phase 0, there is a path labeled by
1798 the values of the variables that ends in one of the final states in the
1802 transition_variables: A non-empty list of variables whose values
1803 correspond to the labels of the arcs traversed by the automaton.
1804 starting_state: The initial state of the automaton.
1805 final_states: A non-empty list of admissible final states.
1806 transition_triples: A list of transitions for the automaton, in the
1807 following format (current_state, variable_value, next_state).
1810 An instance of the `Constraint` class.
1813 ValueError: if `transition_variables`, `final_states`, or
1814 `transition_triples` are empty.
1817 if not transition_variables:
1819 "add_automaton expects a non-empty transition_variables array"
1821 if not final_states:
1822 raise ValueError(
"add_automaton expects some final states")
1824 if not transition_triples:
1825 raise ValueError(
"add_automaton expects some transition triples")
1828 model_ct = self.
__model.constraints[ct.index]
1829 model_ct.automaton.vars.extend(
1832 starting_state = cmh.assert_is_int64(starting_state)
1833 model_ct.automaton.starting_state = starting_state
1834 for v
in final_states:
1835 v = cmh.assert_is_int64(v)
1836 model_ct.automaton.final_states.append(v)
1837 for t
in transition_triples:
1839 raise TypeError(
"Tuple " + str(t) +
" has the wrong arity (!= 3)")
1840 tail = cmh.assert_is_int64(t[0])
1841 label = cmh.assert_is_int64(t[1])
1842 head = cmh.assert_is_int64(t[2])
1843 model_ct.automaton.transition_tail.append(tail)
1844 model_ct.automaton.transition_label.append(label)
1845 model_ct.automaton.transition_head.append(head)
1850 variables: Sequence[VariableT],
1851 inverse_variables: Sequence[VariableT],
1853 """Adds Inverse(variables, inverse_variables).
1855 An inverse constraint enforces that if `variables[i]` is assigned a value
1856 `j`, then `inverse_variables[j]` is assigned a value `i`. And vice versa.
1859 variables: An array of integer variables.
1860 inverse_variables: An array of integer variables.
1863 An instance of the `Constraint` class.
1866 TypeError: if variables and inverse_variables have different lengths, or
1870 if not variables
or not inverse_variables:
1871 raise TypeError(
"The Inverse constraint does not accept empty arrays")
1872 if len(variables) != len(inverse_variables):
1874 "In the inverse constraint, the two array variables and"
1875 " inverse_variables must have the same length."
1878 model_ct = self.
__model.constraints[ct.index]
1879 model_ct.inverse.f_direct.extend([self.
get_or_make_index(x)
for x
in variables])
1880 model_ct.inverse.f_inverse.extend(
1887 times: Iterable[LinearExprT],
1888 level_changes: Iterable[LinearExprT],
1892 """Adds Reservoir(times, level_changes, min_level, max_level).
1894 Maintains a reservoir level within bounds. The water level starts at 0, and
1895 at any time, it must be between min_level and max_level.
1897 If the affine expression `times[i]` is assigned a value t, then the current
1898 level changes by `level_changes[i]`, which is constant, at time t.
1900 Note that min level must be <= 0, and the max level must be >= 0. Please
1901 use fixed level_changes to simulate initial state.
1903 Therefore, at any time:
1904 sum(level_changes[i] if times[i] <= t) in [min_level, max_level]
1907 times: A list of 1-var affine expressions (a * x + b) which specify the
1908 time of the filling or emptying the reservoir.
1909 level_changes: A list of integer values that specifies the amount of the
1910 emptying or filling. Currently, variable demands are not supported.
1911 min_level: At any time, the level of the reservoir must be greater or
1912 equal than the min level.
1913 max_level: At any time, the level of the reservoir must be less or equal
1917 An instance of the `Constraint` class.
1920 ValueError: if max_level < min_level.
1922 ValueError: if max_level < 0.
1924 ValueError: if min_level > 0
1927 if max_level < min_level:
1928 raise ValueError(
"Reservoir constraint must have a max_level >= min_level")
1931 raise ValueError(
"Reservoir constraint must have a max_level >= 0")
1934 raise ValueError(
"Reservoir constraint must have a min_level <= 0")
1937 model_ct = self.
__model.constraints[ct.index]
1938 model_ct.reservoir.time_exprs.extend(
1941 model_ct.reservoir.level_changes.extend(
1944 model_ct.reservoir.min_level = min_level
1945 model_ct.reservoir.max_level = max_level
1950 times: Iterable[LinearExprT],
1951 level_changes: Iterable[LinearExprT],
1952 actives: Iterable[LiteralT],
1956 """Adds Reservoir(times, level_changes, actives, min_level, max_level).
1958 Maintains a reservoir level within bounds. The water level starts at 0, and
1959 at any time, it must be between min_level and max_level.
1961 If the variable `times[i]` is assigned a value t, and `actives[i]` is
1962 `True`, then the current level changes by `level_changes[i]`, which is
1966 Note that min level must be <= 0, and the max level must be >= 0. Please
1967 use fixed level_changes to simulate initial state.
1969 Therefore, at any time:
1970 sum(level_changes[i] * actives[i] if times[i] <= t) in [min_level,
1974 The array of boolean variables 'actives', if defined, indicates which
1975 actions are actually performed.
1978 times: A list of 1-var affine expressions (a * x + b) which specify the
1979 time of the filling or emptying the reservoir.
1980 level_changes: A list of integer values that specifies the amount of the
1981 emptying or filling. Currently, variable demands are not supported.
1982 actives: a list of boolean variables. They indicates if the
1983 emptying/refilling events actually take place.
1984 min_level: At any time, the level of the reservoir must be greater or
1985 equal than the min level.
1986 max_level: At any time, the level of the reservoir must be less or equal
1990 An instance of the `Constraint` class.
1993 ValueError: if max_level < min_level.
1995 ValueError: if max_level < 0.
1997 ValueError: if min_level > 0
2000 if max_level < min_level:
2001 raise ValueError(
"Reservoir constraint must have a max_level >= min_level")
2004 raise ValueError(
"Reservoir constraint must have a max_level >= 0")
2007 raise ValueError(
"Reservoir constraint must have a min_level <= 0")
2010 model_ct = self.
__model.constraints[ct.index]
2011 model_ct.reservoir.time_exprs.extend(
2014 model_ct.reservoir.level_changes.extend(
2017 model_ct.reservoir.active_literals.extend(
2020 model_ct.reservoir.min_level = min_level
2021 model_ct.reservoir.max_level = max_level
2025 self, var: IntVar, bool_var_array: Iterable[IntVar], offset: IntegralT = 0
2027 """Adds `var == i + offset <=> bool_var_array[i] == true for all i`."""
2029 for i, bool_var
in enumerate(bool_var_array):
2030 b_index = bool_var.index
2031 var_index = var.index
2032 model_ct = self.
__model.constraints.add()
2033 model_ct.linear.vars.append(var_index)
2034 model_ct.linear.coeffs.append(1)
2035 offset_as_int = int(offset)
2036 model_ct.linear.domain.extend([offset_as_int + i, offset_as_int + i])
2037 model_ct.enforcement_literal.append(b_index)
2039 model_ct = self.
__model.constraints.add()
2040 model_ct.linear.vars.append(var_index)
2041 model_ct.linear.coeffs.append(1)
2042 model_ct.enforcement_literal.append(-b_index - 1)
2043 if offset + i - 1 >= INT_MIN:
2044 model_ct.linear.domain.extend([INT_MIN, offset_as_int + i - 1])
2045 if offset + i + 1 <= INT_MAX:
2046 model_ct.linear.domain.extend([offset_as_int + i + 1, INT_MAX])
2049 """Adds `a => b` (`a` implies `b`)."""
2051 model_ct = self.
__model.constraints[ct.index]
2057 def add_bool_or(self, literals: Iterable[LiteralT]) -> Constraint: ...
2063 """Adds `Or(literals) == true`: sum(literals) >= 1."""
2065 model_ct = self.
__model.constraints[ct.index]
2066 model_ct.bool_or.literals.extend(
2081 """Same as `add_bool_or`: `sum(literals) >= 1`."""
2091 """Adds `AtMostOne(literals)`: `sum(literals) <= 1`."""
2093 model_ct = self.
__model.constraints[ct.index]
2094 model_ct.at_most_one.literals.extend(
2109 """Adds `ExactlyOne(literals)`: `sum(literals) == 1`."""
2111 model_ct = self.
__model.constraints[ct.index]
2112 model_ct.exactly_one.literals.extend(
2127 """Adds `And(literals) == true`."""
2129 model_ct = self.
__model.constraints[ct.index]
2130 model_ct.bool_and.literals.extend(
2145 """Adds `XOr(literals) == true`.
2147 In contrast to add_bool_or and add_bool_and, it does not support
2151 *literals: the list of literals in the constraint.
2154 An `Constraint` object.
2157 model_ct = self.
__model.constraints[ct.index]
2158 model_ct.bool_xor.literals.extend(
2167 self, target: LinearExprT, exprs: Iterable[LinearExprT]
2169 """Adds `target == Min(exprs)`."""
2171 model_ct = self.
__model.constraints[ct.index]
2172 model_ct.lin_max.exprs.extend(
2179 self, target: LinearExprT, exprs: Iterable[LinearExprT]
2181 """Adds `target == Max(exprs)`."""
2183 model_ct = self.
__model.constraints[ct.index]
2189 self, target: LinearExprT, num: LinearExprT, denom: LinearExprT
2191 """Adds `target == num // denom` (integer division rounded towards 0)."""
2193 model_ct = self.
__model.constraints[ct.index]
2200 """Adds `target == Abs(expr)`."""
2202 model_ct = self.
__model.constraints[ct.index]
2209 self, target: LinearExprT, expr: LinearExprT, mod: LinearExprT
2211 """Adds `target = expr % mod`.
2213 It uses the C convention, that is the result is the remainder of the
2214 integral division rounded towards 0.
2223 target: the target expression.
2224 expr: the expression to compute the modulo of.
2225 mod: the modulus expression.
2228 A `Constraint` object.
2231 model_ct = self.
__model.constraints[ct.index]
2239 target: LinearExprT,
2240 *expressions: Union[Iterable[LinearExprT], LinearExprT],
2242 """Adds `target == expressions[0] * .. * expressions[n]`."""
2244 model_ct = self.
__model.constraints[ct.index]
2245 model_ct.int_prod.exprs.extend(
2257 self, start: LinearExprT, size: LinearExprT, end: LinearExprT, name: str
2259 """Creates an interval variable from start, size, and end.
2261 An interval variable is a constraint, that is itself used in other
2262 constraints like NoOverlap.
2264 Internally, it ensures that `start + size == end`.
2267 start: The start of the interval. It must be of the form a * var + b.
2268 size: The size of the interval. It must be of the form a * var + b.
2269 end: The end of the interval. It must be of the form a * var + b.
2270 name: The name of the interval variable.
2273 An `IntervalVar` object.
2279 if len(start_expr.vars) > 1:
2281 "cp_model.new_interval_var: start must be 1-var affine or constant."
2283 if len(size_expr.vars) > 1:
2285 "cp_model.new_interval_var: size must be 1-var affine or constant."
2287 if len(end_expr.vars) > 1:
2289 "cp_model.new_interval_var: end must be 1-var affine or constant."
2297 starts: Union[LinearExprT, pd.Series],
2298 sizes: Union[LinearExprT, pd.Series],
2299 ends: Union[LinearExprT, pd.Series],
2301 """Creates a series of interval variables with the given name.
2304 name (str): Required. The name of the variable set.
2305 index (pd.Index): Required. The index to use for the variable set.
2306 starts (Union[LinearExprT, pd.Series]): The start of each interval in the
2307 set. If a `pd.Series` is passed in, it will be based on the
2308 corresponding values of the pd.Series.
2309 sizes (Union[LinearExprT, pd.Series]): The size of each interval in the
2310 set. If a `pd.Series` is passed in, it will be based on the
2311 corresponding values of the pd.Series.
2312 ends (Union[LinearExprT, pd.Series]): The ends of each interval in the
2313 set. If a `pd.Series` is passed in, it will be based on the
2314 corresponding values of the pd.Series.
2317 pd.Series: The interval variable set indexed by its corresponding
2321 TypeError: if the `index` is invalid (e.g. a `DataFrame`).
2322 ValueError: if the `name` is not a valid identifier or already exists.
2323 ValueError: if the all the indexes do not match.
2325 if not isinstance(index, pd.Index):
2326 raise TypeError(
"Non-index object is used as index")
2327 if not name.isidentifier():
2328 raise ValueError(
"name={} is not a valid identifier".format(name))
2335 interval_array.append(
2340 name=f
"{name}[{i}]",
2343 return pd.Series(index=index, data=interval_array)
2346 self, start: LinearExprT, size: IntegralT, name: str
2348 """Creates an interval variable from start, and a fixed size.
2350 An interval variable is a constraint, that is itself used in other
2351 constraints like NoOverlap.
2354 start: The start of the interval. It must be of the form a * var + b.
2355 size: The size of the interval. It must be an integer value.
2356 name: The name of the interval variable.
2359 An `IntervalVar` object.
2361 size = cmh.assert_is_int64(size)
2365 if len(start_expr.vars) > 1:
2367 "cp_model.new_interval_var: start must be affine or constant."
2375 starts: Union[LinearExprT, pd.Series],
2376 sizes: Union[IntegralT, pd.Series],
2378 """Creates a series of interval variables with the given name.
2381 name (str): Required. The name of the variable set.
2382 index (pd.Index): Required. The index to use for the variable set.
2383 starts (Union[LinearExprT, pd.Series]): The start of each interval in the
2384 set. If a `pd.Series` is passed in, it will be based on the
2385 corresponding values of the pd.Series.
2386 sizes (Union[IntegralT, pd.Series]): The fixed size of each interval in
2387 the set. If a `pd.Series` is passed in, it will be based on the
2388 corresponding values of the pd.Series.
2391 pd.Series: The interval variable set indexed by its corresponding
2395 TypeError: if the `index` is invalid (e.g. a `DataFrame`).
2396 ValueError: if the `name` is not a valid identifier or already exists.
2397 ValueError: if the all the indexes do not match.
2399 if not isinstance(index, pd.Index):
2400 raise TypeError(
"Non-index object is used as index")
2401 if not name.isidentifier():
2402 raise ValueError(
"name={} is not a valid identifier".format(name))
2408 interval_array.append(
2412 name=f
"{name}[{i}]",
2415 return pd.Series(index=index, data=interval_array)
2422 is_present: LiteralT,
2425 """Creates an optional interval var from start, size, end, and is_present.
2427 An optional interval variable is a constraint, that is itself used in other
2428 constraints like NoOverlap. This constraint is protected by a presence
2429 literal that indicates if it is active or not.
2431 Internally, it ensures that `is_present` implies `start + size ==
2435 start: The start of the interval. It must be of the form a * var + b.
2436 size: The size of the interval. It must be of the form a * var + b.
2437 end: The end of the interval. It must be of the form a * var + b.
2438 is_present: A literal that indicates if the interval is active or not. A
2439 inactive interval is simply ignored by all constraints.
2440 name: The name of the interval variable.
2443 An `IntervalVar` object.
2451 if len(start_expr.vars) > 1:
2453 "cp_model.new_interval_var: start must be affine or constant."
2455 if len(size_expr.vars) > 1:
2457 "cp_model.new_interval_var: size must be affine or constant."
2459 if len(end_expr.vars) > 1:
2461 "cp_model.new_interval_var: end must be affine or constant."
2464 self.
__model, start_expr, size_expr, end_expr, is_present_index, name
2471 starts: Union[LinearExprT, pd.Series],
2472 sizes: Union[LinearExprT, pd.Series],
2473 ends: Union[LinearExprT, pd.Series],
2474 are_present: Union[LiteralT, pd.Series],
2476 """Creates a series of interval variables with the given name.
2479 name (str): Required. The name of the variable set.
2480 index (pd.Index): Required. The index to use for the variable set.
2481 starts (Union[LinearExprT, pd.Series]): The start of each interval in the
2482 set. If a `pd.Series` is passed in, it will be based on the
2483 corresponding values of the pd.Series.
2484 sizes (Union[LinearExprT, pd.Series]): The size of each interval in the
2485 set. If a `pd.Series` is passed in, it will be based on the
2486 corresponding values of the pd.Series.
2487 ends (Union[LinearExprT, pd.Series]): The ends of each interval in the
2488 set. If a `pd.Series` is passed in, it will be based on the
2489 corresponding values of the pd.Series.
2490 are_present (Union[LiteralT, pd.Series]): The performed literal of each
2491 interval in the set. If a `pd.Series` is passed in, it will be based on
2492 the corresponding values of the pd.Series.
2495 pd.Series: The interval variable set indexed by its corresponding
2499 TypeError: if the `index` is invalid (e.g. a `DataFrame`).
2500 ValueError: if the `name` is not a valid identifier or already exists.
2501 ValueError: if the all the indexes do not match.
2503 if not isinstance(index, pd.Index):
2504 raise TypeError(
"Non-index object is used as index")
2505 if not name.isidentifier():
2506 raise ValueError(
"name={} is not a valid identifier".format(name))
2515 interval_array.append(
2520 is_present=are_present[i],
2521 name=f
"{name}[{i}]",
2524 return pd.Series(index=index, data=interval_array)
2530 is_present: LiteralT,
2533 """Creates an interval variable from start, and a fixed size.
2535 An interval variable is a constraint, that is itself used in other
2536 constraints like NoOverlap.
2539 start: The start of the interval. It must be of the form a * var + b.
2540 size: The size of the interval. It must be an integer value.
2541 is_present: A literal that indicates if the interval is active or not. A
2542 inactive interval is simply ignored by all constraints.
2543 name: The name of the interval variable.
2546 An `IntervalVar` object.
2548 size = cmh.assert_is_int64(size)
2552 if len(start_expr.vars) > 1:
2554 "cp_model.new_interval_var: start must be affine or constant."
2570 starts: Union[LinearExprT, pd.Series],
2571 sizes: Union[IntegralT, pd.Series],
2572 are_present: Union[LiteralT, pd.Series],
2574 """Creates a series of interval variables with the given name.
2577 name (str): Required. The name of the variable set.
2578 index (pd.Index): Required. The index to use for the variable set.
2579 starts (Union[LinearExprT, pd.Series]): The start of each interval in the
2580 set. If a `pd.Series` is passed in, it will be based on the
2581 corresponding values of the pd.Series.
2582 sizes (Union[IntegralT, pd.Series]): The fixed size of each interval in
2583 the set. If a `pd.Series` is passed in, it will be based on the
2584 corresponding values of the pd.Series.
2585 are_present (Union[LiteralT, pd.Series]): The performed literal of each
2586 interval in the set. If a `pd.Series` is passed in, it will be based on
2587 the corresponding values of the pd.Series.
2590 pd.Series: The interval variable set indexed by its corresponding
2594 TypeError: if the `index` is invalid (e.g. a `DataFrame`).
2595 ValueError: if the `name` is not a valid identifier or already exists.
2596 ValueError: if the all the indexes do not match.
2598 if not isinstance(index, pd.Index):
2599 raise TypeError(
"Non-index object is used as index")
2600 if not name.isidentifier():
2601 raise ValueError(
"name={} is not a valid identifier".format(name))
2608 interval_array.append(
2612 is_present=are_present[i],
2613 name=f
"{name}[{i}]",
2616 return pd.Series(index=index, data=interval_array)
2619 """Adds NoOverlap(interval_vars).
2621 A NoOverlap constraint ensures that all present intervals do not overlap
2625 interval_vars: The list of interval variables to constrain.
2628 An instance of the `Constraint` class.
2631 model_ct = self.
__model.constraints[ct.index]
2632 model_ct.no_overlap.intervals.extend(
2639 x_intervals: Iterable[IntervalVar],
2640 y_intervals: Iterable[IntervalVar],
2642 """Adds NoOverlap2D(x_intervals, y_intervals).
2644 A NoOverlap2D constraint ensures that all present rectangles do not overlap
2645 on a plane. Each rectangle is aligned with the X and Y axis, and is defined
2646 by two intervals which represent its projection onto the X and Y axis.
2648 Furthermore, one box is optional if at least one of the x or y interval is
2652 x_intervals: The X coordinates of the rectangles.
2653 y_intervals: The Y coordinates of the rectangles.
2656 An instance of the `Constraint` class.
2659 model_ct = self.
__model.constraints[ct.index]
2660 model_ct.no_overlap_2d.x_intervals.extend(
2663 model_ct.no_overlap_2d.y_intervals.extend(
2670 intervals: Iterable[IntervalVar],
2671 demands: Iterable[LinearExprT],
2672 capacity: LinearExprT,
2674 """Adds Cumulative(intervals, demands, capacity).
2676 This constraint enforces that:
2680 if (start(intervals[i]) <= t < end(intervals[i])) and
2681 (intervals[i] is present)) <= capacity
2684 intervals: The list of intervals.
2685 demands: The list of demands for each interval. Each demand must be >= 0.
2686 Each demand can be a 1-var affine expression (a * x + b).
2687 capacity: The maximum capacity of the cumulative constraint. It can be a
2688 1-var affine expression (a * x + b).
2691 An instance of the `Constraint` class.
2694 model_ct = self.
__model.constraints[cumulative.index]
2695 model_ct.cumulative.intervals.extend(
2705 """Reset the model, and creates a new one from a CpModelProto instance."""
2708 clone.rebuild_constant_map()
2712 """Internal method used during model cloning."""
2713 for i, var
in enumerate(self.
__model.variables):
2714 if len(var.domain) == 2
and var.domain[0] == var.domain[1]:
2718 """Returns an already created Boolean variable from its index."""
2719 if index < 0
or index >= len(self.
__model.variables):
2721 f
"get_bool_var_from_proto_index: out of bound index {index}"
2723 var = self.
__model.variables[index]
2724 if len(var.domain) != 2
or var.domain[0] < 0
or var.domain[1] > 1:
2726 f
"get_bool_var_from_proto_index: index {index} does not reference"
2727 +
" a Boolean variable"
2733 """Returns an already created integer variable from its index."""
2734 if index < 0
or index >= len(self.
__model.variables):
2736 f
"get_int_var_from_proto_index: out of bound index {index}"
2741 """Returns an already created interval variable from its index."""
2742 if index < 0
or index >= len(self.
__model.constraints):
2744 f
"get_interval_var_from_proto_index: out of bound index {index}"
2746 ct = self.
__model.constraints[index]
2747 if not ct.HasField(
"interval"):
2749 f
"get_interval_var_from_proto_index: index {index} does not"
2750 " reference an" +
" interval variable"
2761 def proto(self) -> cp_model_pb2.CpModelProto:
2762 """Returns the underlying CpModelProto."""
2769 """Returns the index of a variable, its negation, or a number."""
2770 if isinstance(arg, IntVar):
2773 isinstance(arg, _ProductCst)
2774 and isinstance(arg.expression(), IntVar)
2775 and arg.coefficient() == -1
2777 return -arg.expression().index - 1
2778 if isinstance(arg, IntegralTypes):
2779 arg = cmh.assert_is_int64(arg)
2781 raise TypeError(
"NotSupported: model.get_or_make_index(" + str(arg) +
")")
2784 """Returns an index from a boolean expression."""
2785 if isinstance(arg, IntVar):
2788 if isinstance(arg, _NotBooleanVariable):
2791 if isinstance(arg, IntegralTypes):
2796 arg = cmh.assert_is_zero_or_one(arg)
2798 if cmh.is_boolean(arg):
2800 raise TypeError(f
"not supported: model.get_or_make_boolean_index({arg})")
2803 if not isinstance(arg, IntervalVar):
2804 raise TypeError(
"NotSupported: model.get_interval_index(%s)" % arg)
2810 index = len(self.
__model.variables)
2811 self.
__model.variables.add(domain=[value, value])
2816 self, var_index: int
2817 ) -> cp_model_pb2.IntegerVariableProto:
2819 return self.
__model.variables[var_index]
2821 return self.
__model.variables[-var_index - 1]
2824 self, linear_expr: LinearExprT, negate: bool =
False
2825 ) -> cp_model_pb2.LinearExpressionProto:
2826 """Returns a LinearExpressionProto built from a LinearExpr instance."""
2827 result: cp_model_pb2.LinearExpressionProto = (
2828 cp_model_pb2.LinearExpressionProto()
2830 mult = -1
if negate
else 1
2831 if isinstance(linear_expr, IntegralTypes):
2832 result.offset = int(linear_expr) * mult
2835 if isinstance(linear_expr, IntVar):
2837 result.coeffs.append(mult)
2840 coeffs_map, constant = cast(LinearExpr, linear_expr).get_integer_var_value_map()
2841 result.offset = constant * mult
2842 for t
in coeffs_map.items():
2843 if not isinstance(t[0], IntVar):
2844 raise TypeError(
"Wrong argument" + str(t))
2845 c = cmh.assert_is_int64(t[1])
2846 result.vars.append(t[0].index)
2847 result.coeffs.append(c * mult)
2851 """Sets the objective of the model."""
2853 if isinstance(obj, IntVar):
2854 self.
__model.objective.vars.append(obj.index)
2855 self.
__model.objective.offset = 0
2857 self.
__model.objective.coeffs.append(1)
2858 self.
__model.objective.scaling_factor = 1
2860 self.
__model.objective.coeffs.append(-1)
2861 self.
__model.objective.scaling_factor = -1
2862 elif isinstance(obj, LinearExpr):
2863 coeffs_map, constant, is_integer = obj.get_float_var_value_map()
2866 self.
__model.objective.scaling_factor = 1
2867 self.
__model.objective.offset = constant
2869 self.
__model.objective.scaling_factor = -1
2870 self.
__model.objective.offset = -constant
2871 for v, c
in coeffs_map.items():
2873 self.
__model.objective.vars.append(v.index)
2875 self.
__model.objective.coeffs.append(c_as_int)
2877 self.
__model.objective.coeffs.append(-c_as_int)
2879 self.
__model.floating_point_objective.maximize =
not minimize
2880 self.
__model.floating_point_objective.offset = constant
2881 for v, c
in coeffs_map.items():
2882 self.
__model.floating_point_objective.coeffs.append(c)
2883 self.
__model.floating_point_objective.vars.append(v.index)
2884 elif isinstance(obj, IntegralTypes):
2885 self.
__model.objective.offset = int(obj)
2886 self.
__model.objective.scaling_factor = 1
2888 raise TypeError(
"TypeError: " + str(obj) +
" is not a valid objective")
2891 """Sets the objective of the model to minimize(obj)."""
2895 """Sets the objective of the model to maximize(obj)."""
2899 return self.
__model.HasField(
"objective")
or self.
__model.HasField(
2900 "floating_point_objective"
2904 self.
__model.ClearField(
"objective")
2905 self.
__model.ClearField(
"floating_point_objective")
2909 variables: Sequence[IntVar],
2910 var_strategy: cp_model_pb2.DecisionStrategyProto.VariableSelectionStrategy,
2911 domain_strategy: cp_model_pb2.DecisionStrategyProto.DomainReductionStrategy,
2913 """Adds a search strategy to the model.
2916 variables: a list of variables this strategy will assign.
2917 var_strategy: heuristic to choose the next variable to assign.
2918 domain_strategy: heuristic to reduce the domain of the selected variable.
2919 Currently, this is advanced code: the union of all strategies added to
2920 the model must be complete, i.e. instantiates all variables. Otherwise,
2924 strategy: cp_model_pb2.DecisionStrategyProto = (
2925 self.
__model.search_strategy.add()
2928 expr = strategy.exprs.add()
2930 expr.vars.append(v.index)
2931 expr.coeffs.append(1)
2933 expr.vars.append(self.
negated(v.index))
2934 expr.coeffs.append(-1)
2937 strategy.variable_selection_strategy = var_strategy
2938 strategy.domain_reduction_strategy = domain_strategy
2941 """Returns a string containing some model statistics."""
2942 return swig_helper.CpSatHelper.model_stats(self.
__model)
2945 """Returns a string indicating that the model is invalid."""
2946 return swig_helper.CpSatHelper.validate_model(self.
__model)
2949 """Write the model as a protocol buffer to 'file'.
2952 file: file to write the model to. If the filename ends with 'txt', the
2953 model will be written as a text file, otherwise, the binary format will
2957 True if the model was correctly written.
2959 return swig_helper.CpSatHelper.write_model_to_file(self.
__model, file)
2962 """Adds 'var == value' as a hint to the solver."""
2964 self.
__model.solution_hint.values.append(value)
2967 """Removes any solution hint from the model."""
2968 self.
__model.ClearField(
"solution_hint")
2971 """Adds the literal to the model as assumptions."""
2975 """Adds the literals to the model as assumptions."""
2976 for lit
in literals:
2980 """Removes all assumptions from the model."""
2981 self.
__model.ClearField(
"assumptions")
2985 if isinstance(x, IntVar):
2986 var = self.
__model.variables[x.index]
2987 if len(var.domain) != 2
or var.domain[0] < 0
or var.domain[1] > 1:
2988 raise TypeError(
"TypeError: " + str(x) +
" is not a boolean variable")
2989 elif not isinstance(x, _NotBooleanVariable):
2990 raise TypeError(
"TypeError: " + str(x) +
" is not a boolean variable")
3001 def Proto(self) -> cp_model_pb2.CpModelProto:
3004 NewIntVar = new_int_var
3005 NewIntVarFromDomain = new_int_var_from_domain
3006 NewBoolVar = new_bool_var
3007 NewConstant = new_constant
3008 NewIntVarSeries = new_int_var_series
3009 NewBoolVarSeries = new_bool_var_series
3010 AddLinearConstraint = add_linear_constraint
3011 AddLinearExpressionInDomain = add_linear_expression_in_domain
3013 AddAllDifferent = add_all_different
3014 AddElement = add_element
3015 AddCircuit = add_circuit
3016 AddMultipleCircuit = add_multiple_circuit
3017 AddAllowedAssignments = add_allowed_assignments
3018 AddForbiddenAssignments = add_forbidden_assignments
3019 AddAutomaton = add_automaton
3020 AddInverse = add_inverse
3021 AddReservoirConstraint = add_reservoir_constraint
3022 AddReservoirConstraintWithActive = add_reservoir_constraint_with_active
3023 AddImplication = add_implication
3024 AddBoolOr = add_bool_or
3025 AddAtLeastOne = add_at_least_one
3026 AddAtMostOne = add_at_most_one
3027 AddExactlyOne = add_exactly_one
3028 AddBoolAnd = add_bool_and
3029 AddBoolXOr = add_bool_xor
3030 AddMinEquality = add_min_equality
3031 AddMaxEquality = add_max_equality
3032 AddDivisionEquality = add_division_equality
3033 AddAbsEquality = add_abs_equality
3034 AddModuloEquality = add_modulo_equality
3035 AddMultiplicationEquality = add_multiplication_equality
3036 NewIntervalVar = new_interval_var
3037 NewIntervalVarSeries = new_interval_var_series
3038 NewFixedSizeIntervalVar = new_fixed_size_interval_var
3039 NewOptionalIntervalVar = new_optional_interval_var
3040 NewOptionalIntervalVarSeries = new_optional_interval_var_series
3041 NewOptionalFixedSizeIntervalVar = new_optional_fixed_size_interval_var
3042 NewOptionalFixedSizeIntervalVarSeries = new_optional_fixed_size_interval_var_series
3043 AddNoOverlap = add_no_overlap
3044 AddNoOverlap2D = add_no_overlap_2d
3045 AddCumulative = add_cumulative
3047 GetBoolVarFromProtoIndex = get_bool_var_from_proto_index
3048 GetIntVarFromProtoIndex = get_int_var_from_proto_index
3049 GetIntervalVarFromProtoIndex = get_interval_var_from_proto_index
3052 HasObjective = has_objective
3053 ClearObjective = clear_objective
3054 AddDecisionStrategy = add_decision_strategy
3055 ModelStats = model_stats
3057 ExportToFile = export_to_file
3059 ClearHints = clear_hints
3060 AddAssumption = add_assumption
3061 AddAssumptions = add_assumptions
3062 ClearAssumptions = clear_assumptions
3069 args: Union[Tuple[LiteralT, ...], Iterable[LiteralT]]
3070) -> Union[Iterable[LiteralT], LiteralT]: ...
3075 args: Union[Tuple[LinearExprT, ...], Iterable[LinearExprT]]
3076) -> Union[Iterable[LinearExprT], LinearExprT]: ...
3080 if hasattr(args,
"__len__"):
3083 if isinstance(args[0], (NumberTypes, LinearExpr)):
3090 expression: LinearExprT, solution: cp_model_pb2.CpSolverResponse
3092 """Evaluate a linear expression against a solution."""
3093 if isinstance(expression, IntegralTypes):
3094 return int(expression)
3095 if not isinstance(expression, LinearExpr):
3096 raise TypeError(
"Cannot interpret %s as a linear expression." % expression)
3099 to_process = [(expression, 1)]
3101 expr, coeff = to_process.pop()
3102 if isinstance(expr, IntegralTypes):
3103 value += int(expr) * coeff
3104 elif isinstance(expr, _ProductCst):
3105 to_process.append((expr.expression(), coeff * expr.coefficient()))
3106 elif isinstance(expr, _Sum):
3107 to_process.append((expr.left(), coeff))
3108 to_process.append((expr.right(), coeff))
3109 elif isinstance(expr, _SumArray):
3110 for e
in expr.expressions():
3111 to_process.append((e, coeff))
3112 value += expr.constant() * coeff
3113 elif isinstance(expr, _WeightedSum):
3114 for e, c
in zip(expr.expressions(), expr.coefficients()):
3115 to_process.append((e, coeff * c))
3116 value += expr.constant() * coeff
3117 elif isinstance(expr, IntVar):
3118 value += coeff * solution.solution[expr.index]
3119 elif isinstance(expr, _NotBooleanVariable):
3120 value += coeff * (1 - solution.solution[expr.negated().index])
3122 raise TypeError(f
"Cannot interpret {expr} as a linear expression.")
3128 literal: LiteralT, solution: cp_model_pb2.CpSolverResponse
3130 """Evaluate a boolean expression against a solution."""
3131 if isinstance(literal, IntegralTypes):
3132 return bool(literal)
3133 elif isinstance(literal, IntVar)
or isinstance(literal, _NotBooleanVariable):
3134 index: int = cast(Union[IntVar, _NotBooleanVariable], literal).index
3136 return bool(solution.solution[index])
3138 return not solution.solution[-index - 1]
3140 raise TypeError(f
"Cannot interpret {literal} as a boolean expression.")
3144 """Main solver class.
3146 The purpose of this class is to search for a solution to the model provided
3147 to the solve() method.
3149 Once solve() is called, this class allows inspecting the solution found
3150 with the value() and boolean_value() methods, as well as general statistics
3151 about the solve procedure.
3155 self.
__solution: Optional[cp_model_pb2.CpSolverResponse] =
None
3157 sat_parameters_pb2.SatParameters()
3162 self.
__lock: threading.Lock = threading.Lock()
3167 solution_callback: Optional[
"CpSolverSolutionCallback"] =
None,
3168 ) -> cp_model_pb2.CpSolverStatus:
3169 """Solves a problem and passes each solution to the callback if not null."""
3174 if solution_callback
is not None:
3188 if solution_callback
is not None:
3194 return solution.status
3197 """Stops the current search asynchronously."""
3202 def value(self, expression: LinearExprT) -> int:
3203 """Returns the value of a linear expression after solve."""
3206 def values(self, variables: _IndexOrSeries) -> pd.Series:
3207 """Returns the values of the input variables.
3209 If `variables` is a `pd.Index`, then the output will be indexed by the
3210 variables. If `variables` is a `pd.Series` indexed by the underlying
3211 dimensions, then the output will be indexed by the same underlying
3215 variables (Union[pd.Index, pd.Series]): The set of variables from which to
3219 pd.Series: The values of all variables in the set.
3223 func=
lambda v: solution.solution[v.index],
3228 """Returns the boolean value of a literal after solve."""
3232 """Returns the values of the input variables.
3234 If `variables` is a `pd.Index`, then the output will be indexed by the
3235 variables. If `variables` is a `pd.Series` indexed by the underlying
3236 dimensions, then the output will be indexed by the same underlying
3240 variables (Union[pd.Index, pd.Series]): The set of variables from which to
3244 pd.Series: The values of all variables in the set.
3254 """Returns the value of the objective after solve."""
3259 """Returns the best lower (upper) bound found when min(max)imizing."""
3264 """Returns the number of boolean variables managed by the SAT solver."""
3269 """Returns the number of conflicts since the creation of the solver."""
3274 """Returns the number of search branches explored by the solver."""
3279 """Returns the wall time in seconds since the creation of the solver."""
3284 """Returns the user time in seconds since the creation of the solver."""
3289 """Returns the response object."""
3293 """Returns some statistics on the solution found as a string."""
3297 """Returns the indices of the infeasible assumptions."""
3301 """Returns the name of the status returned by solve()."""
3304 return cp_model_pb2.CpSolverStatus.Name(status)
3307 """Returns some information on the solve process.
3309 Returns some information on how the solution was found, or the reason
3310 why the model or the parameters are invalid.
3313 RuntimeError: if solve() has not been called.
3319 """Checks solve() has been called, and returns the solution."""
3321 raise RuntimeError(
"solve() has not been called.")
3357 solution_callback: Optional[
"CpSolverSolutionCallback"] =
None,
3358 ) -> cp_model_pb2.CpSolverStatus:
3359 return self.
solve(model, solution_callback)
3376 def Value(self, expression: LinearExprT) -> int:
3377 return self.
value(expression)
3379 def Values(self, variables: _IndexOrSeries) -> pd.Series:
3380 return self.
values(variables)
3386 self, model: CpModel, callback:
"CpSolverSolutionCallback"
3387 ) -> cp_model_pb2.CpSolverStatus:
3388 """DEPRECATED Use solve() with the callback argument."""
3390 "solve_with_solution_callback is deprecated; use solve() with"
3391 +
"the callback argument.",
3394 return self.
solve(model, callback)
3397 self, model: CpModel, callback:
"CpSolverSolutionCallback"
3398 ) -> cp_model_pb2.CpSolverStatus:
3399 """DEPRECATED Use solve() with the right parameter.
3401 Search for all solutions of a satisfiability problem.
3403 This method searches for all feasible solutions of a given model.
3404 Then it feeds the solution to the callback.
3406 Note that the model cannot contain an objective.
3409 model: The model to solve.
3410 callback: The callback that will be called at each solution.
3413 The status of the solve:
3415 * *FEASIBLE* if some solutions have been found
3416 * *INFEASIBLE* if the solver has proved there are no solution
3417 * *OPTIMAL* if all solutions have been found
3420 "search_for_all_solutions is deprecated; use solve() with"
3421 +
"enumerate_all_solutions = True.",
3424 if model.has_objective():
3426 "Search for all solutions is only defined on satisfiability problems"
3429 enumerate_all = self.
parameters.enumerate_all_solutions
3430 self.
parameters.enumerate_all_solutions =
True
3432 status: cp_model_pb2.CpSolverStatus = self.
solve(model, callback)
3435 self.
parameters.enumerate_all_solutions = enumerate_all
3443 """Solution callback.
3445 This class implements a callback that will be called at each new solution
3446 found during search.
3448 The method on_solution_callback() will be called by the solver, and must be
3449 implemented. The current solution can be queried using the boolean_value()
3450 and value() methods.
3452 These methods returns the same information as their counterpart in the
3457 swig_helper.SolutionCallback.__init__(self)
3460 """Proxy for the same method in snake case."""
3461 self.on_solution_callback()
3464 """Returns the boolean value of a boolean literal.
3467 lit: A boolean variable or its negation.
3470 The Boolean value of the literal in the solution.
3473 RuntimeError: if `lit` is not a boolean variable or its negation.
3476 raise RuntimeError(
"solve() has not been called.")
3477 if isinstance(lit, IntegralTypes):
3479 if isinstance(lit, IntVar)
or isinstance(lit, _NotBooleanVariable):
3480 return self.SolutionBooleanValue(
3481 cast(Union[IntVar, _NotBooleanVariable], lit).index
3483 if cmh.is_boolean(lit):
3485 raise TypeError(f
"Cannot interpret {lit} as a boolean expression.")
3487 def value(self, expression: LinearExprT) -> int:
3488 """Evaluates an linear expression in the current solution.
3491 expression: a linear expression of the model.
3494 An integer value equal to the evaluation of the linear expression
3495 against the current solution.
3498 RuntimeError: if 'expression' is not a LinearExpr.
3501 raise RuntimeError(
"solve() has not been called.")
3504 to_process = [(expression, 1)]
3506 expr, coeff = to_process.pop()
3507 if isinstance(expr, IntegralTypes):
3508 value += int(expr) * coeff
3509 elif isinstance(expr, _ProductCst):
3510 to_process.append((expr.expression(), coeff * expr.coefficient()))
3511 elif isinstance(expr, _Sum):
3512 to_process.append((expr.left(), coeff))
3513 to_process.append((expr.right(), coeff))
3514 elif isinstance(expr, _SumArray):
3515 for e
in expr.expressions():
3516 to_process.append((e, coeff))
3517 value += expr.constant() * coeff
3518 elif isinstance(expr, _WeightedSum):
3519 for e, c
in zip(expr.expressions(), expr.coefficients()):
3520 to_process.append((e, coeff * c))
3521 value += expr.constant() * coeff
3522 elif isinstance(expr, IntVar):
3523 value += coeff * self.SolutionIntegerValue(expr.index)
3524 elif isinstance(expr, _NotBooleanVariable):
3525 value += coeff * (1 - self.SolutionIntegerValue(expr.negated().index))
3528 f
"cannot interpret {expression} as a linear expression."
3534 return self.HasResponse()
3537 """Stops the current search asynchronously."""
3539 raise RuntimeError(
"solve() has not been called.")
3544 """Returns the value of the objective after solve."""
3546 raise RuntimeError(
"solve() has not been called.")
3547 return self.ObjectiveValue()
3551 """Returns the best lower (upper) bound found when min(max)imizing."""
3553 raise RuntimeError(
"solve() has not been called.")
3554 return self.BestObjectiveBound()
3558 """Returns the number of boolean variables managed by the SAT solver."""
3560 raise RuntimeError(
"solve() has not been called.")
3561 return self.NumBooleans()
3565 """Returns the number of conflicts since the creation of the solver."""
3567 raise RuntimeError(
"solve() has not been called.")
3568 return self.NumConflicts()
3572 """Returns the number of search branches explored by the solver."""
3574 raise RuntimeError(
"solve() has not been called.")
3575 return self.NumBranches()
3579 """Returns the number of integer propagations done by the solver."""
3581 raise RuntimeError(
"solve() has not been called.")
3582 return self.NumIntegerPropagations()
3586 """Returns the number of Boolean propagations done by the solver."""
3588 raise RuntimeError(
"solve() has not been called.")
3589 return self.NumBooleanPropagations()
3593 """Returns the determistic time in seconds since the creation of the solver."""
3595 raise RuntimeError(
"solve() has not been called.")
3596 return self.DeterministicTime()
3600 """Returns the wall time in seconds since the creation of the solver."""
3602 raise RuntimeError(
"solve() has not been called.")
3603 return self.WallTime()
3607 """Returns the user time in seconds since the creation of the solver."""
3609 raise RuntimeError(
"solve() has not been called.")
3610 return self.UserTime()
3614 """Returns the response object."""
3616 raise RuntimeError(
"solve() has not been called.")
3617 return self.Response()
3622 BooleanValue = boolean_value
3627 """Display the objective value and time of intermediate solutions."""
3630 CpSolverSolutionCallback.__init__(self)
3635 """Called on each new solution."""
3636 current_time = time.time()
3639 "Solution %i, time = %0.2f s, objective = %i"
3645 """Returns the number of solutions found."""
3650 """Print intermediate solutions (objective, variable values, time)."""
3652 def __init__(self, variables: Sequence[IntVar]) ->
None:
3653 CpSolverSolutionCallback.__init__(self)
3659 """Called on each new solution."""
3660 current_time = time.time()
3663 "Solution %i, time = %0.2f s, objective = %i"
3667 print(
" %s = %i" % (v, self.
value(v)), end=
" ")
3673 """Returns the number of solutions found."""
3678 """Print intermediate solutions (variable values, time)."""
3680 def __init__(self, variables: Sequence[IntVar]) ->
None:
3681 CpSolverSolutionCallback.__init__(self)
3687 """Called on each new solution."""
3688 current_time = time.time()
3690 "Solution %i, time = %0.2f s"
3694 print(
" %s = %i" % (v, self.
value(v)), end=
" ")
3700 """Returns the number of solutions found."""
3705 """Returns the indices of `obj` as a `pd.Index`."""
3706 if isinstance(obj, pd.Series):
3713 func: Callable[[IntVar], IntegralT],
3714 values: _IndexOrSeries,
3716 """Returns the attributes of `values`.
3719 func: The function to call for getting the attribute data.
3720 values: The values that the function will be applied (element-wise) to.
3723 pd.Series: The attribute values.
3726 data=[func(v)
for v
in values],
3732 value_or_series: Union[IntegralT, pd.Series], index: pd.Index
3734 """Returns a pd.Series of the given index with the corresponding values.
3737 value_or_series: the values to be converted (if applicable).
3738 index: the index of the resulting pd.Series.
3741 pd.Series: The set of values with the given index.
3744 TypeError: If the type of `value_or_series` is not recognized.
3745 ValueError: If the index does not match.
3747 if isinstance(value_or_series, IntegralTypes):
3748 result = pd.Series(data=value_or_series, index=index)
3749 elif isinstance(value_or_series, pd.Series):
3750 if value_or_series.index.equals(index):
3751 result = value_or_series
3753 raise ValueError(
"index does not match")
3755 raise TypeError(
"invalid type={}".format(type(value_or_series)))
3760 value_or_series: Union[LinearExprT, pd.Series], index: pd.Index
3762 """Returns a pd.Series of the given index with the corresponding values.
3765 value_or_series: the values to be converted (if applicable).
3766 index: the index of the resulting pd.Series.
3769 pd.Series: The set of values with the given index.
3772 TypeError: If the type of `value_or_series` is not recognized.
3773 ValueError: If the index does not match.
3775 if isinstance(value_or_series, IntegralTypes):
3776 result = pd.Series(data=value_or_series, index=index)
3777 elif isinstance(value_or_series, pd.Series):
3778 if value_or_series.index.equals(index):
3779 result = value_or_series
3781 raise ValueError(
"index does not match")
3783 raise TypeError(
"invalid type={}".format(type(value_or_series)))
3788 value_or_series: Union[LiteralT, pd.Series], index: pd.Index
3790 """Returns a pd.Series of the given index with the corresponding values.
3793 value_or_series: the values to be converted (if applicable).
3794 index: the index of the resulting pd.Series.
3797 pd.Series: The set of values with the given index.
3800 TypeError: If the type of `value_or_series` is not recognized.
3801 ValueError: If the index does not match.
3803 if isinstance(value_or_series, IntegralTypes):
3804 result = pd.Series(data=value_or_series, index=index)
3805 elif isinstance(value_or_series, pd.Series):
3806 if value_or_series.index.equals(index):
3807 result = value_or_series
3809 raise ValueError(
"index does not match")
3811 raise TypeError(
"invalid type={}".format(type(value_or_series)))