15"""Methods for building and solving CP-SAT models.
17The following two sections describe the main
18methods for building and solving CP-SAT models.
20* [`CpModel`](#cp_model.CpModel): Methods for creating models, including
21 variables and constraints.
22* [`CpSolver`](#cp_model.CpSolver): Methods for solving a model and evaluating
25The following methods implement callbacks that the
26solver calls each time it finds a new solution.
28* [`CpSolverSolutionCallback`](#cp_model.CpSolverSolutionCallback):
29 A general method for implementing callbacks.
30* [`ObjectiveSolutionPrinter`](#cp_model.ObjectiveSolutionPrinter):
31 Print objective values and elapsed time for intermediate solutions.
32* [`VarArraySolutionPrinter`](#cp_model.VarArraySolutionPrinter):
33 Print intermediate solutions (variable values, time).
34* [`VarArrayAndObjectiveSolutionPrinter`]
35 (#cp_model.VarArrayAndObjectiveSolutionPrinter):
36 Print both intermediate solutions and objective values.
38Additional methods for solving CP-SAT models:
40* [`Constraint`](#cp_model.Constraint): A few utility methods for modifying
41 constraints created by `CpModel`.
42* [`LinearExpr`](#lineacp_model.LinearExpr): Methods for creating constraints
43 and the objective from large arrays of coefficients.
45Other methods and functions listed are primarily used for developing OR-Tools,
46rather than for solving specific optimization problems.
49from collections.abc
import Callable, Iterable, Sequence
68BoundedLinearExpression = cmh.BoundedLinearExpression
69Constraint = cmh.Constraint
70CpModelProto = cmh.CpModelProto
71CpSolverResponse = cmh.CpSolverResponse
72CpSolverStatus = cmh.CpSolverStatus
73Domain = sorted_interval_list.Domain
74FlatFloatExpr = cmh.FlatFloatExpr
75FlatIntExpr = cmh.FlatIntExpr
76IntervalVar = cmh.IntervalVar
78LinearExpr = cmh.LinearExpr
79NotBooleanVariable = cmh.NotBooleanVariable
80SatParameters = cmh.SatParameters
93UNKNOWN = cmh.CpSolverStatus.UNKNOWN
94UNKNOWN = cmh.CpSolverStatus.UNKNOWN
95MODEL_INVALID = cmh.CpSolverStatus.MODEL_INVALID
96FEASIBLE = cmh.CpSolverStatus.FEASIBLE
97INFEASIBLE = cmh.CpSolverStatus.INFEASIBLE
98OPTIMAL = cmh.CpSolverStatus.OPTIMAL
101CHOOSE_FIRST = cmh.DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_FIRST
103 cmh.DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_LOWEST_MIN
105CHOOSE_HIGHEST_MAX = (
106 cmh.DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_HIGHEST_MAX
108CHOOSE_MIN_DOMAIN_SIZE = (
109 cmh.DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_MIN_DOMAIN_SIZE
111CHOOSE_MAX_DOMAIN_SIZE = (
112 cmh.DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_MAX_DOMAIN_SIZE
116SELECT_MIN_VALUE = cmh.DecisionStrategyProto.DomainReductionStrategy.SELECT_MIN_VALUE
117SELECT_MAX_VALUE = cmh.DecisionStrategyProto.DomainReductionStrategy.SELECT_MAX_VALUE
118SELECT_LOWER_HALF = cmh.DecisionStrategyProto.DomainReductionStrategy.SELECT_LOWER_HALF
119SELECT_UPPER_HALF = cmh.DecisionStrategyProto.DomainReductionStrategy.SELECT_UPPER_HALF
120SELECT_MEDIAN_VALUE = (
121 cmh.DecisionStrategyProto.DomainReductionStrategy.SELECT_MEDIAN_VALUE
123SELECT_RANDOM_HALF = (
124 cmh.DecisionStrategyProto.DomainReductionStrategy.SELECT_RANDOM_HALF
128AUTOMATIC_SEARCH = cmh.SatParameters.SearchBranching.AUTOMATIC_SEARCH
129FIXED_SEARCH = cmh.SatParameters.SearchBranching.FIXED_SEARCH
130PORTFOLIO_SEARCH = cmh.SatParameters.SearchBranching.PORTFOLIO_SEARCH
131LP_SEARCH = cmh.SatParameters.SearchBranching.LP_SEARCH
132PSEUDO_COST_SEARCH = cmh.SatParameters.SearchBranching.PSEUDO_COST_SEARCH
133PORTFOLIO_WITH_QUICK_RESTART_SEARCH = (
134 cmh.SatParameters.SearchBranching.PORTFOLIO_WITH_QUICK_RESTART_SEARCH
136HINT_SEARCH = cmh.SatParameters.SearchBranching.HINT_SEARCH
137PARTIAL_FIXED_SEARCH = cmh.SatParameters.SearchBranching.PARTIAL_FIXED_SEARCH
138RANDOMIZED_SEARCH = cmh.SatParameters.SearchBranching.RANDOMIZED_SEARCH
141IntegralT = Union[int, np.int8, np.uint8, np.int32, np.uint32, np.int64, np.uint64]
174LiteralT = Union[cmh.Literal, IntegralT, bool]
175BoolVarT = cmh.Literal
176VariableT = Union[
"IntVar", IntegralT]
179LinearExprT = Union[LinearExpr,
"IntVar", IntegralT]
180ObjLinearExprT = Union[LinearExpr, NumberT]
182ArcT = tuple[IntegralT, IntegralT, LiteralT]
183_IndexOrSeries = Union[pd.Index, pd.Series]
187enable_warnings =
False
191def deprecated(message: str) -> Callable[[Callable], Callable]:
192 """Decorator that warns about a deprecated function."""
194 def deprecated_decorator(func) -> Callable:
195 def deprecated_func(*args, **kwargs):
198 f
"{func.__name__} is a deprecated function. {message}",
199 category=DeprecationWarning,
202 warnings.simplefilter(
"default", DeprecationWarning)
203 return func(*args, **kwargs)
205 return deprecated_func
207 return deprecated_decorator
211 """Wrapper that warns about a deprecated method."""
213 def deprecated_func(*args, **kwargs) -> Any:
216 f
"{old_name} is a deprecated function. Use {func.__name__} instead.",
217 category=DeprecationWarning,
220 warnings.simplefilter(
"default", DeprecationWarning)
221 return func(*args, **kwargs)
223 return deprecated_func
230 """Converts a snake_case name to CamelCase."""
231 words = name.split(
"_")
233 "".join(word.capitalize()
for word
in words)
235 .replace(
"Xor",
"XOr")
240 """Checks if literal is either True, or a Boolean literals fixed to True."""
241 if isinstance(literal, IntVar):
242 proto = literal.proto
243 return len(proto.domain) == 2
and proto.domain[0] == 1
and proto.domain[1] == 1
244 if isinstance(literal, cmh.NotBooleanVariable):
245 proto = literal.negated().proto
246 return len(proto.domain) == 2
and proto.domain[0] == 0
and proto.domain[1] == 0
247 if isinstance(literal, (bool, np.bool_)):
249 if isinstance(literal, IntegralTypes):
250 literal_as_int = int(literal)
251 return literal_as_int == 1
or literal_as_int == ~
False
256 """Checks if literal is either False, or a Boolean literals fixed to False."""
257 if isinstance(literal, IntVar):
258 proto = literal.proto
259 return len(proto.domain) == 2
and proto.domain[0] == 0
and proto.domain[1] == 0
260 if isinstance(literal, cmh.NotBooleanVariable):
261 proto = literal.negated().proto
262 return len(proto.domain) == 2
and proto.domain[0] == 1
and proto.domain[1] == 1
263 if isinstance(literal, (bool, np.bool_)):
264 return not bool(literal)
265 if isinstance(literal, IntegralTypes):
266 literal_as_int = int(literal)
267 return literal_as_int == 0
or literal_as_int == ~
True
272 """Returns the indices of `obj` as a `pd.Index`."""
273 if isinstance(obj, pd.Series):
280 value_or_series: Union[LinearExprT, pd.Series], index: pd.Index
286 value_or_series: Union[LiteralT, pd.Series], index: pd.Index
292 value_or_series: Union[IntegralT, pd.Series], index: pd.Index
297 """Returns a pd.Series of the given index with the corresponding values."""
298 if isinstance(value_or_series, pd.Series):
299 if value_or_series.index.equals(index):
300 return value_or_series
302 raise ValueError(
"index does not match")
303 return pd.Series(data=value_or_series, index=index)
307 """Methods for building a CP model.
309 Methods beginning with:
311 * ```new_``` create integer, boolean, or interval variables.
312 * ```add_``` create new constraints and add them to the model.
315 def __init__(self, model_proto: Optional[cmh.CpModelProto] =
None) ->
None:
316 cmh.CpBaseModel.__init__(self, model_proto)
322 """Returns the name of the model."""
329 """Sets the name of the model."""
333 def new_int_var(self, lb: IntegralT, ub: IntegralT, name: str) -> IntVar:
334 """Create an integer variable with domain [lb, ub].
336 The CP-SAT solver is limited to integer variables. If you have fractional
337 values, scale them up so that they become integers; if you have strings,
338 encode them as integers.
341 lb: Lower bound for the variable.
342 ub: Upper bound for the variable.
343 name: The name of the variable.
346 a variable whose domain is [lb, ub].
351 .with_domain(sorted_interval_list.Domain(lb, ub))
355 self, domain: sorted_interval_list.Domain, name: str
357 """Create an integer variable from a domain.
359 A domain is a set of integers specified by a collection of intervals.
360 For example, `model.new_int_var_from_domain(cp_model.
361 Domain.from_intervals([[1, 2], [4, 6]]), 'x')`
364 domain: An instance of the Domain class.
365 name: The name of the variable.
368 a variable whose domain is the given domain.
373 """Creates a 0-1 variable with the given name."""
377 .with_domain(sorted_interval_list.Domain(0, 1))
381 """Declares a constant integer."""
388 lower_bounds: Union[IntegralT, pd.Series],
389 upper_bounds: Union[IntegralT, pd.Series],
391 """Creates a series of (scalar-valued) variables with the given name.
394 name (str): Required. The name of the variable set.
395 index (pd.Index): Required. The index to use for the variable set.
396 lower_bounds (Union[int, pd.Series]): A lower bound for variables in the
397 set. If a `pd.Series` is passed in, it will be based on the
398 corresponding values of the pd.Series.
399 upper_bounds (Union[int, pd.Series]): An upper bound for variables in the
400 set. If a `pd.Series` is passed in, it will be based on the
401 corresponding values of the pd.Series.
404 pd.Series: The variable set indexed by its corresponding dimensions.
407 TypeError: if the `index` is invalid (e.g. a `DataFrame`).
408 ValueError: if the `name` is not a valid identifier or already exists.
409 ValueError: if the `lowerbound` is greater than the `upperbound`.
410 ValueError: if the index of `lower_bound`, or `upper_bound` does not match
413 if not isinstance(index, pd.Index):
414 raise TypeError(
"Non-index object is used as index")
415 if not name.isidentifier():
416 raise ValueError(f
"name={name!r} is not a valid identifier")
418 isinstance(lower_bounds, IntegralTypes)
419 and isinstance(upper_bounds, IntegralTypes)
420 and lower_bounds > upper_bounds
423 f
"lower_bound={lower_bounds} is greater than"
424 f
" upper_bound={upper_bounds} for variable set={name}"
434 .with_name(f
"{name}[{i}]")
436 sorted_interval_list.Domain(lower_bounds[i], upper_bounds[i])
447 """Creates a series of (scalar-valued) variables with the given name.
450 name (str): Required. The name of the variable set.
451 index (pd.Index): Required. The index to use for the variable set.
454 pd.Series: The variable set indexed by its corresponding dimensions.
457 TypeError: if the `index` is invalid (e.g. a `DataFrame`).
458 ValueError: if the `name` is not a valid identifier or already exists.
460 if not isinstance(index, pd.Index):
461 raise TypeError(
"Non-index object is used as index")
462 if not name.isidentifier():
463 raise ValueError(f
"name={name!r} is not a valid identifier")
469 .with_name(f
"{name}[{i}]")
470 .with_domain(sorted_interval_list.Domain(0, 1))
478 self, linear_expr: LinearExprT, lb: IntegralT, ub: IntegralT
480 """Adds the constraint: `lb <= linear_expr <= ub`."""
482 linear_expr, sorted_interval_list.Domain(lb, ub)
487 linear_expr: LinearExprT,
488 domain: sorted_interval_list.Domain,
490 """Adds the constraint: `linear_expr` in `domain`."""
491 if isinstance(linear_expr, LinearExpr):
495 "Cannot add a linear expression containing floating point"
496 f
" coefficients or constants: {type(linear_expr).__name__!r}"
498 return self._add_bounded_linear_expression(ble)
499 if isinstance(linear_expr, IntegralTypes):
500 if not domain.contains(int(linear_expr)):
506 f
" CpModel.add_linear_expression_in_domain({type(linear_expr).__name__!r})"
509 def add(self, ct: Union[BoundedLinearExpression, bool, np.bool_]) -> Constraint:
510 """Adds a `BoundedLinearExpression` to the model.
513 ct: A [`BoundedLinearExpression`](#boundedlinearexpression).
516 An instance of the `Constraint` class.
519 TypeError: If the `ct` is not a `BoundedLinearExpression` or a Boolean.
521 if isinstance(ct, BoundedLinearExpression):
522 return self._add_bounded_linear_expression(ct)
523 if ct
and self.is_boolean_value(ct):
525 if not ct
and self.is_boolean_value(ct):
527 raise TypeError(f
"not supported: CpModel.add({type(ct).__name__!r})")
538 """Adds AllDifferent(expressions).
540 This constraint forces all expressions to have different values.
543 *expressions: simple expressions of the form a * var + constant.
546 An instance of the `Constraint` class.
548 return self._add_all_different(*expressions)
553 expressions: Sequence[LinearExprT],
556 """Adds the element constraint: `expressions[index] == target`.
559 index: The index of the selected expression in the array. It must be an
560 affine expression (a * var + b).
561 expressions: A list of affine expressions.
562 target: The expression constrained to be equal to the selected expression.
563 It must be an affine expression (a * var + b).
566 An instance of the `Constraint` class.
570 raise ValueError(
"add_element expects a non-empty expressions array")
572 if isinstance(index, IntegralTypes):
573 expression: LinearExprT = list(expressions)[int(index)]
574 return self.
add(expression == target)
576 return self._add_element(index, expressions, target)
579 """Adds Circuit(arcs).
581 Adds a circuit constraint from a sparse list of arcs that encode the graph.
583 A circuit is a unique Hamiltonian cycle in a subgraph of the total
584 graph. In case a node 'i' is not in the cycle, then there must be a
585 loop arc 'i -> i' associated with a true literal. Otherwise
586 this constraint will fail.
589 arcs: a list of arcs. An arc is a tuple (source_node, destination_node,
590 literal). The arc is selected in the circuit if the literal is true.
591 Both source_node and destination_node must be integers between 0 and the
595 An instance of the `Constraint` class.
598 ValueError: If the list of arcs is empty.
601 raise ValueError(
"add_circuit expects a non-empty array of arcs")
602 return self._add_circuit(arcs)
605 """Adds a multiple circuit constraint, aka the 'VRP' constraint.
607 The direct graph where arc #i (from tails[i] to head[i]) is present iff
608 literals[i] is true must satisfy this set of properties:
609 - #incoming arcs == 1 except for node 0.
610 - #outgoing arcs == 1 except for node 0.
611 - for node zero, #incoming arcs == #outgoing arcs.
612 - There are no duplicate arcs.
613 - Self-arcs are allowed except for node 0.
614 - There is no cycle in this graph, except through node 0.
617 arcs: a list of arcs. An arc is a tuple (source_node, destination_node,
618 literal). The arc is selected in the circuit if the literal is true.
619 Both source_node and destination_node must be integers between 0 and the
623 An instance of the `Constraint` class.
626 ValueError: If the list of arcs is empty.
629 raise ValueError(
"add_multiple_circuit expects a non-empty array of arcs")
630 return self._add_routes(arcs)
634 expressions: Sequence[LinearExprT],
635 tuples_list: Iterable[Sequence[IntegralT]],
637 """Adds AllowedAssignments(expressions, tuples_list).
639 An AllowedAssignments constraint is a constraint on an array of affine
640 expressions, which requires that when all expressions are assigned values,
642 resulting array equals one of the tuples in `tuple_list`.
645 expressions: A list of affine expressions (a * var + b).
646 tuples_list: A list of admissible tuples. Each tuple must have the same
647 length as the expressions, and the ith value of a tuple corresponds to
651 An instance of the `Constraint` class.
654 TypeError: If a tuple does not have the same size as the list of
656 ValueError: If the array of expressions is empty.
661 "add_allowed_assignments expects a non-empty expressions array"
664 return self._add_table(expressions, tuples_list,
False)
668 expressions: Sequence[LinearExprT],
669 tuples_list: Iterable[Sequence[IntegralT]],
671 """Adds add_forbidden_assignments(expressions, [tuples_list]).
673 A ForbiddenAssignments constraint is a constraint on an array of affine
674 expressions where the list of impossible combinations is provided in the
678 expressions: A list of affine expressions (a * var + b).
679 tuples_list: A list of forbidden tuples. Each tuple must have the same
680 length as the expressions, and the *i*th value of a tuple corresponds to
681 the *i*th expression.
684 An instance of the `Constraint` class.
687 TypeError: If a tuple does not have the same size as the list of
689 ValueError: If the array of expressions is empty.
694 "add_forbidden_assignments expects a non-empty expressions array"
697 return self._add_table(expressions, tuples_list,
True)
701 transition_expressions: Sequence[LinearExprT],
702 starting_state: IntegralT,
703 final_states: Sequence[IntegralT],
704 transition_triples: Sequence[tuple[IntegralT, IntegralT, IntegralT]],
706 """Adds an automaton constraint.
708 An automaton constraint takes a list of affine expressions (a * var + b) (of
709 size *n*), an initial state, a set of final states, and a set of
710 transitions. A transition is a triplet (*tail*, *transition*, *head*), where
711 *tail* and *head* are states, and *transition* is the label of an arc from
712 *head* to *tail*, corresponding to the value of one expression in the list
716 This automaton will be unrolled into a flow with *n* + 1 phases. Each phase
717 contains the possible states of the automaton. The first state contains the
718 initial state. The last phase contains the final states.
720 Between two consecutive phases *i* and *i* + 1, the automaton creates a set
721 of arcs. For each transition (*tail*, *transition*, *head*), it will add
722 an arc from the state *tail* of phase *i* and the state *head* of phase
723 *i* + 1. This arc is labeled by the value *transition* of the expression
724 `expressions[i]`. That is, this arc can only be selected if `expressions[i]`
725 is assigned the value *transition*.
727 A feasible solution of this constraint is an assignment of expressions such
728 that, starting from the initial state in phase 0, there is a path labeled by
729 the values of the expressions that ends in one of the final states in the
733 transition_expressions: A non-empty list of affine expressions (a * var +
734 b) whose values correspond to the labels of the arcs traversed by the
736 starting_state: The initial state of the automaton.
737 final_states: A non-empty list of admissible final states.
738 transition_triples: A list of transitions for the automaton, in the
739 following format (current_state, variable_value, next_state).
742 An instance of the `Constraint` class.
745 ValueError: if `transition_expressions`, `final_states`, or
746 `transition_triples` are empty.
749 if not transition_expressions:
751 "add_automaton expects a non-empty transition_expressions array"
754 raise ValueError(
"add_automaton expects some final states")
756 if not transition_triples:
757 raise ValueError(
"add_automaton expects some transition triples")
759 return self._add_automaton(
760 transition_expressions,
768 variables: Sequence[VariableT],
769 inverse_variables: Sequence[VariableT],
771 """Adds Inverse(variables, inverse_variables).
773 An inverse constraint enforces that if `variables[i]` is assigned a value
774 `j`, then `inverse_variables[j]` is assigned a value `i`. And vice versa.
777 variables: An array of integer variables.
778 inverse_variables: An array of integer variables.
781 An instance of the `Constraint` class.
784 TypeError: if variables and inverse_variables have different lengths, or
788 if not variables
or not inverse_variables:
789 raise TypeError(
"The Inverse constraint does not accept empty arrays")
790 if len(variables) != len(inverse_variables):
792 "In the inverse constraint, the two array variables and"
793 " inverse_variables must have the same length."
795 return self._add_inverse(variables, inverse_variables)
799 times: Sequence[LinearExprT],
800 level_changes: Sequence[LinearExprT],
804 """Adds Reservoir(times, level_changes, min_level, max_level).
806 Maintains a reservoir level within bounds. The water level starts at 0, and
807 at any time, it must be between min_level and max_level.
809 If the affine expression `times[i]` is assigned a value t, then the current
810 level changes by `level_changes[i]`, which is constant, at time t.
812 Note that min level must be <= 0, and the max level must be >= 0. Please
813 use fixed level_changes to simulate initial state.
815 Therefore, at any time:
816 sum(level_changes[i] if times[i] <= t) in [min_level, max_level]
819 times: A list of 1-var affine expressions (a * x + b) which specify the
820 time of the filling or emptying the reservoir.
821 level_changes: A list of integer values that specifies the amount of the
822 emptying or filling. Currently, variable demands are not supported.
823 min_level: At any time, the level of the reservoir must be greater or
824 equal than the min level.
825 max_level: At any time, the level of the reservoir must be less or equal
829 An instance of the `Constraint` class.
832 ValueError: if max_level < min_level.
834 ValueError: if max_level < 0.
836 ValueError: if min_level > 0
839 return self._add_reservoir(
849 times: Sequence[LinearExprT],
850 level_changes: Sequence[LinearExprT],
851 actives: Sequence[LiteralT],
855 """Adds Reservoir(times, level_changes, actives, min_level, max_level).
857 Maintains a reservoir level within bounds. The water level starts at 0, and
858 at any time, it must be between min_level and max_level.
860 If the variable `times[i]` is assigned a value t, and `actives[i]` is
861 `True`, then the current level changes by `level_changes[i]`, which is
865 Note that min level must be <= 0, and the max level must be >= 0. Please
866 use fixed level_changes to simulate initial state.
868 Therefore, at any time:
869 sum(level_changes[i] * actives[i] if times[i] <= t) in [min_level,
873 The array of boolean variables 'actives', if defined, indicates which
874 actions are actually performed.
877 times: A list of 1-var affine expressions (a * x + b) which specify the
878 time of the filling or emptying the reservoir.
879 level_changes: A list of integer values that specifies the amount of the
880 emptying or filling. Currently, variable demands are not supported.
881 actives: a list of boolean variables. They indicates if the
882 emptying/refilling events actually take place.
883 min_level: At any time, the level of the reservoir must be greater or
884 equal than the min level.
885 max_level: At any time, the level of the reservoir must be less or equal
889 An instance of the `Constraint` class.
892 ValueError: if max_level < min_level.
894 ValueError: if max_level < 0.
896 ValueError: if min_level > 0
899 if max_level < min_level:
900 raise ValueError(
"Reservoir constraint must have a max_level >= min_level")
903 raise ValueError(
"Reservoir constraint must have a max_level >= 0")
906 raise ValueError(
"Reservoir constraint must have a min_level <= 0")
909 raise ValueError(
"Reservoir constraint must have a non-empty times array")
911 return self._add_reservoir(
920 self, var: IntVar, bool_var_array: Iterable[IntVar], offset: IntegralT = 0
922 """Adds `var == i + offset <=> bool_var_array[i] == true for all i`."""
923 for i, bool_var
in enumerate(bool_var_array):
924 self.
add(var == i + offset).only_enforce_if(bool_var)
925 self.
add(var != i + offset).only_enforce_if(~bool_var)
928 """Adds `a => b` (`a` implies `b`)."""
932 def add_bool_or(self, literals: Iterable[LiteralT]) -> Constraint: ...
938 """Adds `Or(literals) == true`: sum(literals) >= 1."""
939 return self._add_bool_argument_constraint(
940 cmh.BoolArgumentConstraint.bool_or, *literals
950 """Same as `add_bool_or`: `sum(literals) >= 1`."""
951 return self._add_bool_argument_constraint(
952 cmh.BoolArgumentConstraint.bool_or, *literals
962 """Adds `AtMostOne(literals)`: `sum(literals) <= 1`."""
963 return self._add_bool_argument_constraint(
964 cmh.BoolArgumentConstraint.at_most_one, *literals
974 """Adds `ExactlyOne(literals)`: `sum(literals) == 1`."""
975 return self._add_bool_argument_constraint(
976 cmh.BoolArgumentConstraint.exactly_one, *literals
980 def add_bool_and(self, literals: Iterable[LiteralT]) -> Constraint: ...
986 """Adds `And(literals) == true`."""
987 return self._add_bool_argument_constraint(
988 cmh.BoolArgumentConstraint.bool_and, *literals
992 def add_bool_xor(self, literals: Iterable[LiteralT]) -> Constraint: ...
998 """Adds `XOr(literals) == true`.
1000 In contrast to add_bool_or and add_bool_and, it does not support
1004 *literals: the list of literals in the constraint.
1007 An `Constraint` object.
1009 return self._add_bool_argument_constraint(
1010 cmh.BoolArgumentConstraint.bool_xor, *literals
1015 self, target: LinearExprT, expressions: Iterable[LinearExprT]
1016 ) -> Constraint: ...
1020 self, target: LinearExprT, *expressions: LinearExprT
1021 ) -> Constraint: ...
1024 """Adds `target == Min(expressions)`."""
1025 return self._add_linear_argument_constraint(
1026 cmh.LinearArgumentConstraint.min, target, *expressions
1031 self, target: LinearExprT, expressions: Iterable[LinearExprT]
1032 ) -> Constraint: ...
1036 self, target: LinearExprT, *expressions: LinearExprT
1037 ) -> Constraint: ...
1040 """Adds `target == Max(expressions)`."""
1041 return self._add_linear_argument_constraint(
1042 cmh.LinearArgumentConstraint.max, target, *expressions
1046 self, target: LinearExprT, num: LinearExprT, denom: LinearExprT
1048 """Adds `target == num // denom` (integer division rounded towards 0)."""
1049 return self._add_linear_argument_constraint(
1050 cmh.LinearArgumentConstraint.div, target, [num, denom]
1054 """Adds `target == Abs(expr)`."""
1055 return self._add_linear_argument_constraint(
1056 cmh.LinearArgumentConstraint.max, target, [expr, -expr]
1060 self, target: LinearExprT, expr: LinearExprT, mod: LinearExprT
1062 """Adds `target = expr % mod`.
1064 It uses the C convention, that is the result is the remainder of the
1065 integral division rounded towards 0.
1074 target: the target expression.
1075 expr: the expression to compute the modulo of.
1076 mod: the modulus expression.
1079 A `Constraint` object.
1081 return self._add_linear_argument_constraint(
1082 cmh.LinearArgumentConstraint.mod, target, [expr, mod]
1087 target: LinearExprT,
1088 *expressions: Union[Iterable[LinearExprT], LinearExprT],
1090 """Adds `target == expressions[0] * .. * expressions[n]`."""
1091 return self._add_linear_argument_constraint(
1092 cmh.LinearArgumentConstraint.prod, target, *expressions
1098 self, start: LinearExprT, size: LinearExprT, end: LinearExprT, name: str
1100 """Creates an interval variable from start, size, and end.
1102 An interval variable is a constraint, that is itself used in other
1103 constraints like NoOverlap.
1105 Internally, it ensures that `start + size == end`.
1108 start: The start of the interval. It must be of the form a * var + b.
1109 size: The size of the interval. It must be of the form a * var + b.
1110 end: The end of the interval. It must be of the form a * var + b.
1111 name: The name of the interval variable.
1114 An `IntervalVar` object.
1116 return self._new_interval_var(name, start, size, end, [])
1122 starts: Union[LinearExprT, pd.Series],
1123 sizes: Union[LinearExprT, pd.Series],
1124 ends: Union[LinearExprT, pd.Series],
1126 """Creates a series of interval variables with the given name.
1129 name (str): Required. The name of the variable set.
1130 index (pd.Index): Required. The index to use for the variable set.
1131 starts (Union[LinearExprT, pd.Series]): The start of each interval in the
1132 set. If a `pd.Series` is passed in, it will be based on the
1133 corresponding values of the pd.Series.
1134 sizes (Union[LinearExprT, pd.Series]): The size of each interval in the
1135 set. If a `pd.Series` is passed in, it will be based on the
1136 corresponding values of the pd.Series.
1137 ends (Union[LinearExprT, pd.Series]): The ends of each interval in the
1138 set. If a `pd.Series` is passed in, it will be based on the
1139 corresponding values of the pd.Series.
1142 pd.Series: The interval variable set indexed by its corresponding
1146 TypeError: if the `index` is invalid (e.g. a `DataFrame`).
1147 ValueError: if the `name` is not a valid identifier or already exists.
1148 ValueError: if the all the indexes do not match.
1150 if not isinstance(index, pd.Index):
1151 raise TypeError(
"Non-index object is used as index")
1152 if not name.isidentifier():
1153 raise ValueError(f
"name={name!r} is not a valid identifier")
1160 interval_array.append(
1165 name=f
"{name}[{i}]",
1168 return pd.Series(index=index, data=interval_array)
1171 self, start: LinearExprT, size: IntegralT, name: str
1173 """Creates an interval variable from start, and a fixed size.
1175 An interval variable is a constraint, that is itself used in other
1176 constraints like NoOverlap.
1179 start: The start of the interval. It must be of the form a * var + b.
1180 size: The size of the interval. It must be an integer value.
1181 name: The name of the interval variable.
1184 An `IntervalVar` object.
1186 return self._new_interval_var(name, start, size, start + size, [])
1192 starts: Union[LinearExprT, pd.Series],
1193 sizes: Union[IntegralT, pd.Series],
1195 """Creates a series of interval variables with the given name.
1198 name (str): Required. The name of the variable set.
1199 index (pd.Index): Required. The index to use for the variable set.
1200 starts (Union[LinearExprT, pd.Series]): The start of each interval in the
1201 set. If a `pd.Series` is passed in, it will be based on the
1202 corresponding values of the pd.Series.
1203 sizes (Union[IntegralT, pd.Series]): The fixed size of each interval in
1204 the set. If a `pd.Series` is passed in, it will be based on the
1205 corresponding values of the pd.Series.
1208 pd.Series: The interval variable set indexed by its corresponding
1212 TypeError: if the `index` is invalid (e.g. a `DataFrame`).
1213 ValueError: if the `name` is not a valid identifier or already exists.
1214 ValueError: if the all the indexes do not match.
1216 if not isinstance(index, pd.Index):
1217 raise TypeError(
"Non-index object is used as index")
1218 if not name.isidentifier():
1219 raise ValueError(f
"name={name!r} is not a valid identifier")
1225 interval_array.append(
1229 name=f
"{name}[{i}]",
1232 return pd.Series(index=index, data=interval_array)
1239 is_present: LiteralT,
1242 """Creates an optional interval var from start, size, end, and is_present.
1244 An optional interval variable is a constraint, that is itself used in other
1245 constraints like NoOverlap. This constraint is protected by a presence
1246 literal that indicates if it is active or not.
1248 Internally, it ensures that `is_present` implies `start + size ==
1252 start: The start of the interval. It must be of the form a * var + b.
1253 size: The size of the interval. It must be of the form a * var + b.
1254 end: The end of the interval. It must be of the form a * var + b.
1255 is_present: A literal that indicates if the interval is active or not. A
1256 inactive interval is simply ignored by all constraints.
1257 name: The name of the interval variable.
1260 An `IntervalVar` object.
1262 return self._new_interval_var(
1274 starts: Union[LinearExprT, pd.Series],
1275 sizes: Union[LinearExprT, pd.Series],
1276 ends: Union[LinearExprT, pd.Series],
1277 are_present: Union[LiteralT, pd.Series],
1279 """Creates a series of interval variables with the given name.
1282 name (str): Required. The name of the variable set.
1283 index (pd.Index): Required. The index to use for the variable set.
1284 starts (Union[LinearExprT, pd.Series]): The start of each interval in the
1285 set. If a `pd.Series` is passed in, it will be based on the
1286 corresponding values of the pd.Series.
1287 sizes (Union[LinearExprT, pd.Series]): The size of each interval in the
1288 set. If a `pd.Series` is passed in, it will be based on the
1289 corresponding values of the pd.Series.
1290 ends (Union[LinearExprT, pd.Series]): The ends of each interval in the
1291 set. If a `pd.Series` is passed in, it will be based on the
1292 corresponding values of the pd.Series.
1293 are_present (Union[LiteralT, pd.Series]): The performed literal of each
1294 interval in the set. If a `pd.Series` is passed in, it will be based on
1295 the corresponding values of the pd.Series.
1298 pd.Series: The interval variable set indexed by its corresponding
1302 TypeError: if the `index` is invalid (e.g. a `DataFrame`).
1303 ValueError: if the `name` is not a valid identifier or already exists.
1304 ValueError: if the all the indexes do not match.
1306 if not isinstance(index, pd.Index):
1307 raise TypeError(
"Non-index object is used as index")
1308 if not name.isidentifier():
1309 raise ValueError(f
"name={name!r} is not a valid identifier")
1318 interval_array.append(
1323 is_present=are_present[i],
1324 name=f
"{name}[{i}]",
1327 return pd.Series(index=index, data=interval_array)
1333 is_present: LiteralT,
1336 """Creates an interval variable from start, and a fixed size.
1338 An interval variable is a constraint, that is itself used in other
1339 constraints like NoOverlap.
1342 start: The start of the interval. It must be of the form a * var + b.
1343 size: The size of the interval. It must be an integer value.
1344 is_present: A literal that indicates if the interval is active or not. A
1345 inactive interval is simply ignored by all constraints.
1346 name: The name of the interval variable.
1349 An `IntervalVar` object.
1351 return self._new_interval_var(
1363 starts: Union[LinearExprT, pd.Series],
1364 sizes: Union[IntegralT, pd.Series],
1365 are_present: Union[LiteralT, pd.Series],
1367 """Creates a series of interval variables with the given name.
1370 name (str): Required. The name of the variable set.
1371 index (pd.Index): Required. The index to use for the variable set.
1372 starts (Union[LinearExprT, pd.Series]): The start of each interval in the
1373 set. If a `pd.Series` is passed in, it will be based on the
1374 corresponding values of the pd.Series.
1375 sizes (Union[IntegralT, pd.Series]): The fixed size of each interval in
1376 the set. If a `pd.Series` is passed in, it will be based on the
1377 corresponding values of the pd.Series.
1378 are_present (Union[LiteralT, pd.Series]): The performed literal of each
1379 interval in the set. If a `pd.Series` is passed in, it will be based on
1380 the corresponding values of the pd.Series.
1383 pd.Series: The interval variable set indexed by its corresponding
1387 TypeError: if the `index` is invalid (e.g. a `DataFrame`).
1388 ValueError: if the `name` is not a valid identifier or already exists.
1389 ValueError: if the all the indexes do not match.
1391 if not isinstance(index, pd.Index):
1392 raise TypeError(
"Non-index object is used as index")
1393 if not name.isidentifier():
1394 raise ValueError(f
"name={name!r} is not a valid identifier")
1401 interval_array.append(
1405 is_present=are_present[i],
1406 name=f
"{name}[{i}]",
1409 return pd.Series(index=index, data=interval_array)
1412 """Adds NoOverlap(interval_vars).
1414 A NoOverlap constraint ensures that all present intervals do not overlap
1418 intervals: The list of interval variables to constrain.
1421 An instance of the `Constraint` class.
1423 return self._add_no_overlap(intervals)
1427 x_intervals: Iterable[IntervalVar],
1428 y_intervals: Iterable[IntervalVar],
1430 """Adds NoOverlap2D(x_intervals, y_intervals).
1432 A NoOverlap2D constraint ensures that all present rectangles do not overlap
1433 on a plane. Each rectangle is aligned with the X and Y axis, and is defined
1434 by two intervals which represent its projection onto the X and Y axis.
1436 Furthermore, one box is optional if at least one of the x or y interval is
1440 x_intervals: The X coordinates of the rectangles.
1441 y_intervals: The Y coordinates of the rectangles.
1444 An instance of the `Constraint` class.
1446 return self._add_no_overlap_2d(x_intervals, y_intervals)
1450 intervals: Iterable[IntervalVar],
1451 demands: Iterable[LinearExprT],
1452 capacity: LinearExprT,
1454 """Adds Cumulative(intervals, demands, capacity).
1456 This constraint enforces that:
1460 if (start(intervals[i]) <= t < end(intervals[i])) and
1461 (intervals[i] is present)) <= capacity
1464 intervals: The list of intervals.
1465 demands: The list of demands for each interval. Each demand must be >= 0.
1466 Each demand can be a 1-var affine expression (a * x + b).
1467 capacity: The maximum capacity of the cumulative constraint. It can be a
1468 1-var affine expression (a * x + b).
1471 An instance of the `Constraint` class.
1473 return self._add_cumulative(intervals, demands, capacity)
1477 """Reset the model, and creates a new one from a CpModelProto instance."""
1480 clone.rebuild_constant_map()
1490 """Returns an already created Boolean variable from its index."""
1491 if index < 0
or index >= len(self.
model_proto.variables):
1493 f
"get_bool_var_from_proto_index: out of bound index {index}"
1496 if not result.is_boolean:
1498 f
"get_bool_var_from_proto_index: index {index} does not reference a"
1504 """Returns an already created integer variable from its index."""
1505 if index < 0
or index >= len(self.
model_proto.variables):
1507 f
"get_int_var_from_proto_index: out of bound index {index}"
1512 """Returns an already created interval variable from its index."""
1513 if index < 0
or index >= len(self.
model_proto.constraints):
1515 f
"get_interval_var_from_proto_index: out of bound index {index}"
1518 if not ct.has_interval():
1520 f
"get_interval_var_from_proto_index: index {index} does not"
1521 " reference an" +
" interval variable"
1531 """Returns the underlying CpModelProto."""
1538 """Sets the objective of the model."""
1540 if isinstance(obj, IntegralTypes):
1543 elif isinstance(obj, LinearExpr):
1544 if obj.is_integer():
1545 int_obj = cmh.FlatIntExpr(obj)
1546 for var
in int_obj.vars:
1550 self.
model_proto.objective.offset = -int_obj.offset
1551 for c
in int_obj.coeffs:
1555 self.
model_proto.objective.offset = int_obj.offset
1556 self.
model_proto.objective.coeffs.extend(int_obj.coeffs)
1558 float_obj = cmh.FlatFloatExpr(obj)
1559 for var
in float_obj.vars:
1560 self.
model_proto.floating_point_objective.vars.append(var.index)
1561 self.
model_proto.floating_point_objective.coeffs.extend(
1564 self.
model_proto.floating_point_objective.maximize = maximize
1565 self.
model_proto.floating_point_objective.offset = float_obj.offset
1568 f
"TypeError: {type(obj).__name__!r} is not a valid objective"
1572 """Sets the objective of the model to minimize(obj)."""
1576 """Sets the objective of the model to maximize(obj)."""
1582 or self.
model_proto.has_floating_point_objective()
1591 variables: Iterable[IntVar],
1592 var_strategy: cmh.DecisionStrategyProto.VariableSelectionStrategy,
1593 domain_strategy: cmh.DecisionStrategyProto.DomainReductionStrategy,
1595 """Adds a search strategy to the model.
1598 variables: a list of variables this strategy will assign.
1599 var_strategy: heuristic to choose the next variable to assign.
1600 domain_strategy: heuristic to reduce the domain of the selected variable.
1601 Currently, this is advanced code: the union of all strategies added to
1602 the model must be complete, i.e. instantiates all variables. Otherwise,
1606 strategy: cmh.DecisionStrategyProto = self.
model_proto.search_strategy.add()
1608 expr = strategy.exprs.add()
1610 expr.vars.append(v.index)
1611 expr.coeffs.append(1)
1613 expr.vars.append(self.
negated(v.index))
1614 expr.coeffs.append(-1)
1617 strategy.variable_selection_strategy = var_strategy
1618 strategy.domain_reduction_strategy = domain_strategy
1621 """Returns a string containing some model statistics."""
1622 return cmh.CpSatHelper.model_stats(self.
model_proto)
1625 """Returns a string indicating that the model is invalid."""
1626 return cmh.CpSatHelper.validate_model(self.
model_proto)
1629 """Write the model as a protocol buffer to 'file'.
1632 file: file to write the model to. If the filename ends with 'txt', the
1633 model will be written as a text file, otherwise, the binary format will
1637 True if the model was correctly written.
1639 return cmh.CpSatHelper.write_model_to_file(self.
model_proto, file)
1642 """Removes all names from the model."""
1650 def add_hint(self, var: IntVar, value: int) ->
None: ...
1653 def add_hint(self, literal: BoolVarT, value: bool) ->
None: ...
1656 """Adds 'var == value' as a hint to the solver."""
1658 self.
model_proto.solution_hint.vars.append(var.index)
1659 self.
model_proto.solution_hint.values.append(int(value))
1662 self.
model_proto.solution_hint.values.append(int(
not value))
1665 """Removes any solution hint from the model."""
1669 """Adds the literal to the model as assumptions."""
1670 self.
model_proto.assumptions.append(self.get_or_make_boolean_index(lit))
1673 """Adds the literals to the model as assumptions."""
1674 for lit
in literals:
1678 """Removes all assumptions from the model."""
1685 for method_name
in dir(self):
1686 if callable(getattr(self, method_name))
and (
1687 method_name.startswith(
"add_")
1688 or method_name.startswith(
"new_")
1689 or method_name.startswith(
"clear_")
1698 for other_method_name
in [
1701 "get_bool_var_from_proto_index",
1702 "get_int_var_from_proto_index",
1703 "get_interval_var_from_proto_index",
1718 @deprecated("Use name property instead.")
1722 @deprecated("Use name property instead.")
1726 @deprecated("Use proto property instead.")
1734 """Main solver class.
1736 The purpose of this class is to search for a solution to the model provided
1737 to the solve() method.
1739 Once solve() is called, this class allows inspecting the solution found
1740 with the value() and boolean_value() methods, as well as general statistics
1741 about the solve procedure.
1745 self.
__response: Optional[cmh.CpSolverResponse] =
None
1750 self.
__lock: threading.Lock = threading.Lock()
1755 solution_callback: Optional[
"CpSolverSolutionCallback"] =
None,
1756 ) -> cmh.CpSolverStatus:
1757 """Solves a problem and passes each solution to the callback if not null."""
1762 if solution_callback
is not None:
1773 if solution_callback
is not None:
1782 """Stops the current search asynchronously."""
1787 def value(self, expression: LinearExprT) -> int:
1788 """Returns the value of a linear expression after solve."""
1791 def values(self, variables: _IndexOrSeries) -> pd.Series:
1792 """Returns the values of the input variables.
1794 If `variables` is a `pd.Index`, then the output will be indexed by the
1795 variables. If `variables` is a `pd.Series` indexed by the underlying
1796 dimensions, then the output will be indexed by the same underlying
1800 variables (Union[pd.Index, pd.Series]): The set of variables from which to
1804 pd.Series: The values of all variables in the set.
1807 RuntimeError: if solve() has not been called.
1811 data=[cmh.ResponseHelper.value(response, var)
for var
in variables],
1816 """Returns the value of a linear expression after solve."""
1820 """Returns the float values of the input linear expressions.
1822 If `expressions` is a `pd.Index`, then the output will be indexed by the
1823 variables. If `variables` is a `pd.Series` indexed by the underlying
1824 dimensions, then the output will be indexed by the same underlying
1828 expressions (Union[pd.Index, pd.Series]): The set of expressions from
1829 which to get the values.
1832 pd.Series: The values of all variables in the set.
1835 RuntimeError: if solve() has not been called.
1840 cmh.ResponseHelper.float_value(response, expr)
for expr
in expressions
1846 """Returns the boolean value of a literal after solve."""
1850 """Returns the values of the input variables.
1852 If `variables` is a `pd.Index`, then the output will be indexed by the
1853 variables. If `variables` is a `pd.Series` indexed by the underlying
1854 dimensions, then the output will be indexed by the same underlying
1858 variables (Union[pd.Index, pd.Series]): The set of variables from which to
1862 pd.Series: The values of all variables in the set.
1865 RuntimeError: if solve() has not been called.
1870 cmh.ResponseHelper.boolean_value(response, literal)
1871 for literal
in variables
1878 """Returns the value of the objective after solve."""
1883 """Returns the best lower (upper) bound found when min(max)imizing."""
1888 """Returns the number of boolean variables managed by the SAT solver."""
1893 """Returns the number of conflicts since the creation of the solver."""
1898 """Returns the number of search branches explored by the solver."""
1903 """Returns the number of Boolean propagations done by the solver."""
1908 """Returns the number of integer propagations done by the solver."""
1913 """Returns the deterministic time in seconds since the creation of the solver."""
1918 """Returns the wall time in seconds since the creation of the solver."""
1923 """Returns the user time in seconds since the creation of the solver."""
1928 """Returns the solve log.
1930 To enable this, the parameter log_to_response must be set to True.
1936 """Returns the information about the solve."""
1941 """Returns the response object."""
1945 """Returns some statistics on the solution found as a string."""
1949 """Returns the indices of the infeasible assumptions."""
1950 return cmh.ResponseHelper.sufficient_assumptions_for_infeasibility(
1955 """Returns the name of the status returned by solve()."""
1961 """Returns some information on the solve process.
1963 Returns some information on how the solution was found, or the reason
1964 why the model or the parameters are invalid.
1967 RuntimeError: if solve() has not been called.
1973 """Checks solve() has been called, and returns a response wrapper."""
1975 raise RuntimeError(
"solve() has not been called.")
1981 @deprecated("Use best_objective_bound property instead.")
1985 @deprecated("Use boolean_value() method instead.
")
1989 @deprecated("Use boolean_values() method instead.
")
1993 @deprecated("Use num_booleans property instead.")
1997 @deprecated("Use num_conflicts property instead.")
2001 @deprecated("Use num_branches property instead.")
2005 @deprecated("Use objective_value property instead.")
2009 @deprecated("Use response_proto property instead.")
2013 @deprecated("Use response_stats() method instead.
")
2017 @deprecated("Use solve() method instead.
")
2019 self, model: CpModel, callback:
"CpSolverSolutionCallback" =
None
2020 ) -> cmh.CpSolverStatus:
2021 return self.
solve(model, callback)
2023 @deprecated("Use solution_info() method instead.
")
2027 @deprecated("Use status_name() method instead.
")
2031 @deprecated("Use stop_search() method instead.
")
2035 @deprecated("Use sufficient_assumptions_for_infeasibility() method instead.
")
2039 @deprecated("Use user_time property instead.")
2043 @deprecated("Use value() method instead.
")
2044 def Value(self, expression: LinearExprT) -> int:
2045 return self.
value(expression)
2047 @deprecated("Use values() method instead.
")
2048 def Values(self, expressions: _IndexOrSeries) -> pd.Series:
2049 return self.
values(expressions)
2051 @deprecated("Use wall_time property instead.")
2055 @deprecated("Use solve() with enumerate_all_solutions =
True.
")
2057 self, model: CpModel, callback:
"CpSolverSolutionCallback"
2058 ) -> cmh.CpSolverStatus:
2059 """Search for all solutions of a satisfiability problem.
2061 This method searches for all feasible solutions of a given model.
2062 Then it feeds the solution to the callback.
2064 Note that the model cannot contain an objective.
2067 model: The model to solve.
2068 callback: The callback that will be called at each solution.
2071 The status of the solve:
2073 * *FEASIBLE* if some solutions have been found
2074 * *INFEASIBLE* if the solver has proved there are no solution
2075 * *OPTIMAL* if all solutions have been found
2077 if model.has_objective():
2079 "Search for all solutions is only defined on satisfiability problems"
2082 enumerate_all = self.
parameters.enumerate_all_solutions
2083 self.
parameters.enumerate_all_solutions =
True
2085 status: cmh.CpSolverStatus = self.
solve(model, callback)
2088 self.
parameters.enumerate_all_solutions = enumerate_all
2096 """Solution callback.
2098 This class implements a callback that will be called at each new solution
2099 found during search.
2101 The method on_solution_callback() will be called by the solver, and must be
2102 implemented. The current solution can be queried using the boolean_value()
2103 and value() methods.
2105 These methods returns the same information as their counterpart in the
2110 cmh.SolutionCallback.__init__(self)
2114 """Proxy for the same method in snake case."""
2115 self.on_solution_callback()
2120 """Returns the boolean value of a boolean literal.
2123 lit: A boolean variable or its negation.
2126 The Boolean value of the literal in the solution.
2129 RuntimeError: if `lit` is not a boolean variable or its negation.
2132 raise RuntimeError(
"solve() has not been called.")
2133 return self.BooleanValue(lit)
2135 def value(self, expression: LinearExprT) -> int:
2136 """Evaluates an linear expression in the current solution.
2139 expression: a linear expression of the model.
2142 An integer value equal to the evaluation of the linear expression
2143 against the current solution.
2146 RuntimeError: if 'expression' is not a LinearExpr.
2149 raise RuntimeError(
"solve() has not been called.")
2150 return self.Value(expression)
2153 """Evaluates an linear expression in the current solution.
2156 expression: a linear expression of the model.
2159 An integer value equal to the evaluation of the linear expression
2160 against the current solution.
2163 RuntimeError: if 'expression' is not a LinearExpr.
2166 raise RuntimeError(
"solve() has not been called.")
2167 return self.FloatValue(expression)
2170 return self.HasResponse()
2173 """Stops the current search asynchronously."""
2175 raise RuntimeError(
"solve() has not been called.")
2180 """Returns the value of the objective after solve."""
2182 raise RuntimeError(
"solve() has not been called.")
2183 return self.ObjectiveValue()
2187 """Returns the best lower (upper) bound found when min(max)imizing."""
2189 raise RuntimeError(
"solve() has not been called.")
2190 return self.BestObjectiveBound()
2194 """Returns the number of boolean variables managed by the SAT solver."""
2196 raise RuntimeError(
"solve() has not been called.")
2197 return self.NumBooleans()
2201 """Returns the number of conflicts since the creation of the solver."""
2203 raise RuntimeError(
"solve() has not been called.")
2204 return self.NumConflicts()
2208 """Returns the number of search branches explored by the solver."""
2210 raise RuntimeError(
"solve() has not been called.")
2211 return self.NumBranches()
2215 """Returns the number of integer propagations done by the solver."""
2217 raise RuntimeError(
"solve() has not been called.")
2218 return self.NumIntegerPropagations()
2222 """Returns the number of Boolean propagations done by the solver."""
2224 raise RuntimeError(
"solve() has not been called.")
2225 return self.NumBinaryPropagations()
2229 """Returns the determistic time in seconds since the creation of the solver."""
2231 raise RuntimeError(
"solve() has not been called.")
2232 return self.DeterministicTime()
2236 """Returns the wall time in seconds since the creation of the solver."""
2238 raise RuntimeError(
"solve() has not been called.")
2239 return self.WallTime()
2243 """Returns the user time in seconds since the creation of the solver."""
2245 raise RuntimeError(
"solve() has not been called.")
2246 return self.UserTime()
2250 """Returns the response object."""
2252 raise RuntimeError(
"solve() has not been called.")
2253 return self.Response()
2257 """Display the objective value and time of intermediate solutions."""
2260 CpSolverSolutionCallback.__init__(self)
2265 """Called on each new solution."""
2266 current_time = time.time()
2269 f
"Solution {self.__solution_count}, time ="
2270 f
" {current_time - self.__start_time:0.2f} s, objective = {obj}",
2276 """Returns the number of solutions found."""
2281 """Print intermediate solutions (objective, variable values, time)."""
2283 def __init__(self, variables: Sequence[IntVar]) ->
None:
2284 CpSolverSolutionCallback.__init__(self)
2290 """Called on each new solution."""
2291 current_time = time.time()
2294 f
"Solution {self.__solution_count}, time ="
2295 f
" {current_time - self.__start_time:0.2f} s, objective = {obj}"
2298 print(f
" {v} = {self.value(v)}", end=
" ")
2304 """Returns the number of solutions found."""
2309 """Print intermediate solutions (variable values, time)."""
2311 def __init__(self, variables: Sequence[IntVar]) ->
None:
2312 CpSolverSolutionCallback.__init__(self)
2318 """Called on each new solution."""
2319 current_time = time.time()
2321 f
"Solution {self.__solution_count}, time ="
2322 f
" {current_time - self.__start_time:0.2f} s"
2325 print(f
" {v} = {self.value(v)}", end=
" ")
2331 """Returns the number of solutions found."""