ortools.sat.python.cp_model

Methods for building and solving CP-SAT models.

The following two sections describe the main methods for building and solving CP-SAT models.

  • .CpModel">CpModel: Methods for creating models, including variables and constraints.
  • .CpSolver">CpSolver: Methods for solving a model and evaluating solutions.

The following methods implement callbacks that the solver calls each time it finds a new solution.

Additional methods for solving CP-SAT models:

  • .Constraint">Constraint: A few utility methods for modifying constraints created by CpModel.
  • .LinearExpr">LinearExpr: Methods for creating constraints and the objective from large arrays of coefficients.

Other methods and functions listed are primarily used for developing OR-Tools, rather than for solving specific optimization problems.

   1#!/usr/bin/env python3
   2# Copyright 2010-2025 Google LLC
   3# Licensed under the Apache License, Version 2.0 (the "License");
   4# you may not use this file except in compliance with the License.
   5# You may obtain a copy of the License at
   6#
   7#     http://www.apache.org/licenses/LICENSE-2.0
   8#
   9# Unless required by applicable law or agreed to in writing, software
  10# distributed under the License is distributed on an "AS IS" BASIS,
  11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12# See the License for the specific language governing permissions and
  13# limitations under the License.
  14
  15"""Methods for building and solving CP-SAT models.
  16
  17The following two sections describe the main
  18methods for building and solving CP-SAT models.
  19
  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
  23  solutions.
  24
  25The following methods implement callbacks that the
  26solver calls each time it finds a new solution.
  27
  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.
  37
  38Additional methods for solving CP-SAT models:
  39
  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.
  44
  45Other methods and functions listed are primarily used for developing OR-Tools,
  46rather than for solving specific optimization problems.
  47"""
  48
  49from collections.abc import Callable, Iterable, Sequence
  50import copy
  51import threading
  52import time
  53from typing import (
  54    Any,
  55    Optional,
  56    Union,
  57    overload,
  58)
  59import warnings
  60
  61import numpy as np
  62import pandas as pd
  63
  64from ortools.sat.python import cp_model_helper as cmh
  65from ortools.util.python import sorted_interval_list
  66
  67# Import external types.
  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
  77IntVar = cmh.IntVar
  78LinearExpr = cmh.LinearExpr
  79NotBooleanVariable = cmh.NotBooleanVariable
  80SatParameters = cmh.SatParameters
  81
  82
  83# The classes below allow linear expressions to be expressed naturally with the
  84# usual arithmetic operators + - * / and with constant numbers, which makes the
  85# python API very intuitive. See../ samples/*.py for examples.
  86
  87INT_MIN = -(2**63)  # hardcoded to be platform independent.
  88INT_MAX = 2**63 - 1
  89INT32_MIN = -(2**31)
  90INT32_MAX = 2**31 - 1
  91
  92# CpSolver status (exported to avoid importing cp_model_cp2).
  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
  99
 100# Variable selection strategy
 101CHOOSE_FIRST = cmh.DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_FIRST
 102CHOOSE_LOWEST_MIN = (
 103    cmh.DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_LOWEST_MIN
 104)
 105CHOOSE_HIGHEST_MAX = (
 106    cmh.DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_HIGHEST_MAX
 107)
 108CHOOSE_MIN_DOMAIN_SIZE = (
 109    cmh.DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_MIN_DOMAIN_SIZE
 110)
 111CHOOSE_MAX_DOMAIN_SIZE = (
 112    cmh.DecisionStrategyProto.VariableSelectionStrategy.CHOOSE_MAX_DOMAIN_SIZE
 113)
 114
 115# Domain reduction strategy
 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
 122)
 123SELECT_RANDOM_HALF = (
 124    cmh.DecisionStrategyProto.DomainReductionStrategy.SELECT_RANDOM_HALF
 125)
 126
 127# Search branching
 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
 135)
 136HINT_SEARCH = cmh.SatParameters.SearchBranching.HINT_SEARCH
 137PARTIAL_FIXED_SEARCH = cmh.SatParameters.SearchBranching.PARTIAL_FIXED_SEARCH
 138RANDOMIZED_SEARCH = cmh.SatParameters.SearchBranching.RANDOMIZED_SEARCH
 139
 140# Type aliases
 141IntegralT = Union[int, np.int8, np.uint8, np.int32, np.uint32, np.int64, np.uint64]
 142IntegralTypes = (
 143    int,
 144    np.int8,
 145    np.uint8,
 146    np.int32,
 147    np.uint32,
 148    np.int64,
 149    np.uint64,
 150)
 151NumberT = Union[
 152    int,
 153    float,
 154    np.int8,
 155    np.uint8,
 156    np.int32,
 157    np.uint32,
 158    np.int64,
 159    np.uint64,
 160    np.double,
 161]
 162NumberTypes = (
 163    int,
 164    float,
 165    np.int8,
 166    np.uint8,
 167    np.int32,
 168    np.uint32,
 169    np.int64,
 170    np.uint64,
 171    np.double,
 172)
 173
 174LiteralT = Union[cmh.Literal, IntegralT, bool]
 175BoolVarT = cmh.Literal
 176VariableT = Union["IntVar", IntegralT]
 177
 178# We need to add 'IntVar' for pytype.
 179LinearExprT = Union[LinearExpr, "IntVar", IntegralT]
 180ObjLinearExprT = Union[LinearExpr, NumberT]
 181
 182ArcT = tuple[IntegralT, IntegralT, LiteralT]
 183_IndexOrSeries = Union[pd.Index, pd.Series]
 184
 185
 186# Helper functions.
 187enable_warnings = False
 188
 189# warnings.deprecated is python3.13+. Not compatible with Open Source (3.10+).
 190# pylint: disable=g-bare-generic
 191def deprecated(message: str) -> Callable[[Callable], Callable]:
 192    """Decorator that warns about a deprecated function."""
 193
 194    def deprecated_decorator(func) -> Callable:
 195        def deprecated_func(*args, **kwargs):
 196            if enable_warnings:
 197                warnings.warn(
 198                    f"{func.__name__} is a deprecated function. {message}",
 199                    category=DeprecationWarning,
 200                    stacklevel=2,
 201                )
 202                warnings.simplefilter("default", DeprecationWarning)
 203            return func(*args, **kwargs)
 204
 205        return deprecated_func
 206
 207    return deprecated_decorator
 208
 209
 210def deprecated_method(func, old_name: str) -> Callable:
 211    """Wrapper that warns about a deprecated method."""
 212
 213    def deprecated_func(*args, **kwargs) -> Any:
 214        if enable_warnings:
 215            warnings.warn(
 216                f"{old_name} is a deprecated function. Use {func.__name__} instead.",
 217                category=DeprecationWarning,
 218                stacklevel=2,
 219            )
 220            warnings.simplefilter("default", DeprecationWarning)
 221        return func(*args, **kwargs)
 222
 223    return deprecated_func
 224
 225
 226# pylint: enable=g-bare-generic
 227
 228
 229def snake_case_to_camel_case(name: str) -> str:
 230    """Converts a snake_case name to CamelCase."""
 231    words = name.split("_")
 232    return (
 233        "".join(word.capitalize() for word in words)
 234        .replace("2d", "2D")
 235        .replace("Xor", "XOr")
 236    )
 237
 238
 239def object_is_a_true_literal(literal: LiteralT) -> bool:
 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_)):
 248        return bool(literal)
 249    if isinstance(literal, IntegralTypes):
 250        literal_as_int = int(literal)
 251        return literal_as_int == 1 or literal_as_int == ~False
 252    return False
 253
 254
 255def object_is_a_false_literal(literal: LiteralT) -> bool:
 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
 268    return False
 269
 270
 271def _get_index(obj: _IndexOrSeries) -> pd.Index:
 272    """Returns the indices of `obj` as a `pd.Index`."""
 273    if isinstance(obj, pd.Series):
 274        return obj.index
 275    return obj
 276
 277
 278@overload
 279def _convert_to_series_and_validate_index(
 280    value_or_series: Union[LinearExprT, pd.Series], index: pd.Index
 281) -> pd.Series: ...
 282
 283
 284@overload
 285def _convert_to_series_and_validate_index(
 286    value_or_series: Union[LiteralT, pd.Series], index: pd.Index
 287) -> pd.Series: ...
 288
 289
 290@overload
 291def _convert_to_series_and_validate_index(
 292    value_or_series: Union[IntegralT, pd.Series], index: pd.Index
 293) -> pd.Series: ...
 294
 295
 296def _convert_to_series_and_validate_index(value_or_series, 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
 301        else:
 302            raise ValueError("index does not match")
 303    return pd.Series(data=value_or_series, index=index)
 304
 305
 306class CpModel(cmh.CpBaseModel):
 307    """Methods for building a CP model.
 308
 309    Methods beginning with:
 310
 311    * ```new_``` create integer, boolean, or interval variables.
 312    * ```add_``` create new constraints and add them to the model.
 313    """
 314
 315    def __init__(self, model_proto: Optional[cmh.CpModelProto] = None) -> None:
 316        cmh.CpBaseModel.__init__(self, model_proto)
 317        self._add_pre_pep8_methods()
 318
 319    # Naming.
 320    @property
 321    def name(self) -> str:
 322        """Returns the name of the model."""
 323        if not self.model_proto or not self.model_proto.name:
 324            return ""
 325        return self.model_proto.name
 326
 327    @name.setter
 328    def name(self, name: str):
 329        """Sets the name of the model."""
 330        self.model_proto.name = name
 331
 332    # Integer variable.
 333    def new_int_var(self, lb: IntegralT, ub: IntegralT, name: str) -> IntVar:
 334        """Create an integer variable with domain [lb, ub].
 335
 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.
 339
 340        Args:
 341          lb: Lower bound for the variable.
 342          ub: Upper bound for the variable.
 343          name: The name of the variable.
 344
 345        Returns:
 346          a variable whose domain is [lb, ub].
 347        """
 348        return (
 349            IntVar(self.model_proto)
 350            .with_name(name)
 351            .with_domain(sorted_interval_list.Domain(lb, ub))
 352        )
 353
 354    def new_int_var_from_domain(
 355        self, domain: sorted_interval_list.Domain, name: str
 356    ) -> IntVar:
 357        """Create an integer variable from a domain.
 358
 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')`
 362
 363        Args:
 364          domain: An instance of the Domain class.
 365          name: The name of the variable.
 366
 367        Returns:
 368            a variable whose domain is the given domain.
 369        """
 370        return IntVar(self.model_proto).with_name(name).with_domain(domain)
 371
 372    def new_bool_var(self, name: str) -> IntVar:
 373        """Creates a 0-1 variable with the given name."""
 374        return (
 375            IntVar(self.model_proto)
 376            .with_name(name)
 377            .with_domain(sorted_interval_list.Domain(0, 1))
 378        )
 379
 380    def new_constant(self, value: IntegralT) -> IntVar:
 381        """Declares a constant integer."""
 382        return IntVar(self.model_proto, self.get_or_make_index_from_constant(value))
 383
 384    def new_int_var_series(
 385        self,
 386        name: str,
 387        index: pd.Index,
 388        lower_bounds: Union[IntegralT, pd.Series],
 389        upper_bounds: Union[IntegralT, pd.Series],
 390    ) -> pd.Series:
 391        """Creates a series of (scalar-valued) variables with the given name.
 392
 393        Args:
 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.
 402
 403        Returns:
 404          pd.Series: The variable set indexed by its corresponding dimensions.
 405
 406        Raises:
 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
 411          the input index.
 412        """
 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")
 417        if (
 418            isinstance(lower_bounds, IntegralTypes)
 419            and isinstance(upper_bounds, IntegralTypes)
 420            and lower_bounds > upper_bounds
 421        ):
 422            raise ValueError(
 423                f"lower_bound={lower_bounds} is greater than"
 424                f" upper_bound={upper_bounds} for variable set={name}"
 425            )
 426
 427        lower_bounds = _convert_to_series_and_validate_index(lower_bounds, index)
 428        upper_bounds = _convert_to_series_and_validate_index(upper_bounds, index)
 429        return pd.Series(
 430            index=index,
 431            data=[
 432                # pylint: disable=g-complex-comprehension
 433                IntVar(self.model_proto)
 434                .with_name(f"{name}[{i}]")
 435                .with_domain(
 436                    sorted_interval_list.Domain(lower_bounds[i], upper_bounds[i])
 437                )
 438                for i in index
 439            ],
 440        )
 441
 442    def new_bool_var_series(
 443        self,
 444        name: str,
 445        index: pd.Index,
 446    ) -> pd.Series:
 447        """Creates a series of (scalar-valued) variables with the given name.
 448
 449        Args:
 450          name (str): Required. The name of the variable set.
 451          index (pd.Index): Required. The index to use for the variable set.
 452
 453        Returns:
 454          pd.Series: The variable set indexed by its corresponding dimensions.
 455
 456        Raises:
 457          TypeError: if the `index` is invalid (e.g. a `DataFrame`).
 458          ValueError: if the `name` is not a valid identifier or already exists.
 459        """
 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")
 464        return pd.Series(
 465            index=index,
 466            data=[
 467                # pylint: disable=g-complex-comprehension
 468                IntVar(self.model_proto)
 469                .with_name(f"{name}[{i}]")
 470                .with_domain(sorted_interval_list.Domain(0, 1))
 471                for i in index
 472            ],
 473        )
 474
 475    # Linear constraints.
 476
 477    def add_linear_constraint(
 478        self, linear_expr: LinearExprT, lb: IntegralT, ub: IntegralT
 479    ) -> Constraint:
 480        """Adds the constraint: `lb <= linear_expr <= ub`."""
 481        return self.add_linear_expression_in_domain(
 482            linear_expr, sorted_interval_list.Domain(lb, ub)
 483        )
 484
 485    def add_linear_expression_in_domain(
 486        self,
 487        linear_expr: LinearExprT,
 488        domain: sorted_interval_list.Domain,
 489    ) -> Constraint:
 490        """Adds the constraint: `linear_expr` in `domain`."""
 491        if isinstance(linear_expr, LinearExpr):
 492            ble = BoundedLinearExpression(linear_expr, domain)
 493            if not ble.ok:
 494                raise TypeError(
 495                    "Cannot add a linear expression containing floating point"
 496                    f" coefficients or constants: {type(linear_expr).__name__!r}"
 497                )
 498            return self._add_bounded_linear_expression(ble)
 499        if isinstance(linear_expr, IntegralTypes):
 500            if not domain.contains(int(linear_expr)):
 501                return self.add_bool_or([])  # Evaluate to false.
 502            else:
 503                return self.add_bool_and([])  # Evaluate to true.
 504        raise TypeError(
 505            "not supported:"
 506            f" CpModel.add_linear_expression_in_domain({type(linear_expr).__name__!r})"
 507        )
 508
 509    def add(self, ct: Union[BoundedLinearExpression, bool, np.bool_]) -> Constraint:
 510        """Adds a `BoundedLinearExpression` to the model.
 511
 512        Args:
 513          ct: A [`BoundedLinearExpression`](#boundedlinearexpression).
 514
 515        Returns:
 516          An instance of the `Constraint` class.
 517
 518        Raises:
 519          TypeError: If the `ct` is not a `BoundedLinearExpression` or a Boolean.
 520        """
 521        if isinstance(ct, BoundedLinearExpression):
 522            return self._add_bounded_linear_expression(ct)
 523        if ct and self.is_boolean_value(ct):
 524            return self.add_bool_or([True])
 525        if not ct and self.is_boolean_value(ct):
 526            return self.add_bool_or([])  # Evaluate to false.
 527        raise TypeError(f"not supported: CpModel.add({type(ct).__name__!r})")
 528
 529    # General Integer Constraints.
 530
 531    @overload
 532    def add_all_different(self, expressions: Iterable[LinearExprT]) -> Constraint: ...
 533
 534    @overload
 535    def add_all_different(self, *expressions: LinearExprT) -> Constraint: ...
 536
 537    def add_all_different(self, *expressions):
 538        """Adds AllDifferent(expressions).
 539
 540        This constraint forces all expressions to have different values.
 541
 542        Args:
 543          *expressions: simple expressions of the form a * var + constant.
 544
 545        Returns:
 546          An instance of the `Constraint` class.
 547        """
 548        return self._add_all_different(*expressions)
 549
 550    def add_element(
 551        self,
 552        index: LinearExprT,
 553        expressions: Sequence[LinearExprT],
 554        target: LinearExprT,
 555    ) -> Constraint:
 556        """Adds the element constraint: `expressions[index] == target`.
 557
 558        Args:
 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).
 564
 565        Returns:
 566          An instance of the `Constraint` class.
 567        """
 568
 569        if not expressions:
 570            raise ValueError("add_element expects a non-empty expressions array")
 571
 572        if isinstance(index, IntegralTypes):
 573            expression: LinearExprT = list(expressions)[int(index)]
 574            return self.add(expression == target)
 575
 576        return self._add_element(index, expressions, target)
 577
 578    def add_circuit(self, arcs: Sequence[ArcT]) -> Constraint:
 579        """Adds Circuit(arcs).
 580
 581        Adds a circuit constraint from a sparse list of arcs that encode the graph.
 582
 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.
 587
 588        Args:
 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
 592            number of nodes - 1.
 593
 594        Returns:
 595          An instance of the `Constraint` class.
 596
 597        Raises:
 598          ValueError: If the list of arcs is empty.
 599        """
 600        if not arcs:
 601            raise ValueError("add_circuit expects a non-empty array of arcs")
 602        return self._add_circuit(arcs)
 603
 604    def add_multiple_circuit(self, arcs: Sequence[ArcT]) -> Constraint:
 605        """Adds a multiple circuit constraint, aka the 'VRP' constraint.
 606
 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.
 615
 616        Args:
 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
 620            number of nodes - 1.
 621
 622        Returns:
 623          An instance of the `Constraint` class.
 624
 625        Raises:
 626          ValueError: If the list of arcs is empty.
 627        """
 628        if not arcs:
 629            raise ValueError("add_multiple_circuit expects a non-empty array of arcs")
 630        return self._add_routes(arcs)
 631
 632    def add_allowed_assignments(
 633        self,
 634        expressions: Sequence[LinearExprT],
 635        tuples_list: Iterable[Sequence[IntegralT]],
 636    ) -> Constraint:
 637        """Adds AllowedAssignments(expressions, tuples_list).
 638
 639        An AllowedAssignments constraint is a constraint on an array of affine
 640        expressions, which requires that when all expressions are assigned values,
 641        the
 642        resulting array equals one of the  tuples in `tuple_list`.
 643
 644        Args:
 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
 648            the ith expression.
 649
 650        Returns:
 651          An instance of the `Constraint` class.
 652
 653        Raises:
 654          TypeError: If a tuple does not have the same size as the list of
 655              expressions.
 656          ValueError: If the array of expressions is empty.
 657        """
 658
 659        if not expressions:
 660            raise ValueError(
 661                "add_allowed_assignments expects a non-empty expressions array"
 662            )
 663
 664        return self._add_table(expressions, tuples_list, False)
 665
 666    def add_forbidden_assignments(
 667        self,
 668        expressions: Sequence[LinearExprT],
 669        tuples_list: Iterable[Sequence[IntegralT]],
 670    ) -> Constraint:
 671        """Adds add_forbidden_assignments(expressions, [tuples_list]).
 672
 673        A ForbiddenAssignments constraint is a constraint on an array of affine
 674        expressions where the list of impossible combinations is provided in the
 675        tuples list.
 676
 677        Args:
 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.
 682
 683        Returns:
 684          An instance of the `Constraint` class.
 685
 686        Raises:
 687          TypeError: If a tuple does not have the same size as the list of
 688                     expressions.
 689          ValueError: If the array of expressions is empty.
 690        """
 691
 692        if not expressions:
 693            raise ValueError(
 694                "add_forbidden_assignments expects a non-empty expressions array"
 695            )
 696
 697        return self._add_table(expressions, tuples_list, True)
 698
 699    def add_automaton(
 700        self,
 701        transition_expressions: Sequence[LinearExprT],
 702        starting_state: IntegralT,
 703        final_states: Sequence[IntegralT],
 704        transition_triples: Sequence[tuple[IntegralT, IntegralT, IntegralT]],
 705    ) -> Constraint:
 706        """Adds an automaton constraint.
 707
 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
 713        of
 714        expressions.
 715
 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.
 719
 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*.
 726
 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
 730        final phase.
 731
 732        Args:
 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
 735            automaton.
 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).
 740
 741        Returns:
 742          An instance of the `Constraint` class.
 743
 744        Raises:
 745          ValueError: if `transition_expressions`, `final_states`, or
 746            `transition_triples` are empty.
 747        """
 748
 749        if not transition_expressions:
 750            raise ValueError(
 751                "add_automaton expects a non-empty transition_expressions array"
 752            )
 753        if not final_states:
 754            raise ValueError("add_automaton expects some final states")
 755
 756        if not transition_triples:
 757            raise ValueError("add_automaton expects some transition triples")
 758
 759        return self._add_automaton(
 760            transition_expressions,
 761            starting_state,
 762            final_states,
 763            transition_triples,
 764        )
 765
 766    def add_inverse(
 767        self,
 768        variables: Sequence[VariableT],
 769        inverse_variables: Sequence[VariableT],
 770    ) -> Constraint:
 771        """Adds Inverse(variables, inverse_variables).
 772
 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.
 775
 776        Args:
 777          variables: An array of integer variables.
 778          inverse_variables: An array of integer variables.
 779
 780        Returns:
 781          An instance of the `Constraint` class.
 782
 783        Raises:
 784          TypeError: if variables and inverse_variables have different lengths, or
 785              if they are empty.
 786        """
 787
 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):
 791            raise TypeError(
 792                "In the inverse constraint, the two array variables and"
 793                " inverse_variables must have the same length."
 794            )
 795        return self._add_inverse(variables, inverse_variables)
 796
 797    def add_reservoir_constraint(
 798        self,
 799        times: Sequence[LinearExprT],
 800        level_changes: Sequence[LinearExprT],
 801        min_level: int,
 802        max_level: int,
 803    ) -> Constraint:
 804        """Adds Reservoir(times, level_changes, min_level, max_level).
 805
 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.
 808
 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.
 811
 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.
 814
 815         Therefore, at any time:
 816             sum(level_changes[i] if times[i] <= t) in [min_level, max_level]
 817
 818        Args:
 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
 826            than the max level.
 827
 828        Returns:
 829          An instance of the `Constraint` class.
 830
 831        Raises:
 832          ValueError: if max_level < min_level.
 833
 834          ValueError: if max_level < 0.
 835
 836          ValueError: if min_level > 0
 837        """
 838
 839        return self._add_reservoir(
 840            times,
 841            level_changes,
 842            [],
 843            min_level,
 844            max_level,
 845        )
 846
 847    def add_reservoir_constraint_with_active(
 848        self,
 849        times: Sequence[LinearExprT],
 850        level_changes: Sequence[LinearExprT],
 851        actives: Sequence[LiteralT],
 852        min_level: int,
 853        max_level: int,
 854    ) -> Constraint:
 855        """Adds Reservoir(times, level_changes, actives, min_level, max_level).
 856
 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.
 859
 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
 862        constant,
 863        at time t.
 864
 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.
 867
 868         Therefore, at any time:
 869             sum(level_changes[i] * actives[i] if times[i] <= t) in [min_level,
 870             max_level]
 871
 872
 873        The array of boolean variables 'actives', if defined, indicates which
 874        actions are actually performed.
 875
 876        Args:
 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
 886            than the max level.
 887
 888        Returns:
 889          An instance of the `Constraint` class.
 890
 891        Raises:
 892          ValueError: if max_level < min_level.
 893
 894          ValueError: if max_level < 0.
 895
 896          ValueError: if min_level > 0
 897        """
 898
 899        if max_level < min_level:
 900            raise ValueError("Reservoir constraint must have a max_level >= min_level")
 901
 902        if max_level < 0:
 903            raise ValueError("Reservoir constraint must have a max_level >= 0")
 904
 905        if min_level > 0:
 906            raise ValueError("Reservoir constraint must have a min_level <= 0")
 907
 908        if not times:
 909            raise ValueError("Reservoir constraint must have a non-empty times array")
 910
 911        return self._add_reservoir(
 912            times,
 913            level_changes,
 914            actives,
 915            min_level,
 916            max_level,
 917        )
 918
 919    def add_map_domain(
 920        self, var: IntVar, bool_var_array: Iterable[IntVar], offset: IntegralT = 0
 921    ):
 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)
 926
 927    def add_implication(self, a: LiteralT, b: LiteralT) -> Constraint:
 928        """Adds `a => b` (`a` implies `b`)."""
 929        return self.add_bool_and(b).only_enforce_if(a)
 930
 931    @overload
 932    def add_bool_or(self, literals: Iterable[LiteralT]) -> Constraint: ...
 933
 934    @overload
 935    def add_bool_or(self, *literals: LiteralT) -> Constraint: ...
 936
 937    def add_bool_or(self, *literals):
 938        """Adds `Or(literals) == true`: sum(literals) >= 1."""
 939        return self._add_bool_argument_constraint(
 940            cmh.BoolArgumentConstraint.bool_or, *literals
 941        )
 942
 943    @overload
 944    def add_at_least_one(self, literals: Iterable[LiteralT]) -> Constraint: ...
 945
 946    @overload
 947    def add_at_least_one(self, *literals: LiteralT) -> Constraint: ...
 948
 949    def add_at_least_one(self, *literals):
 950        """Same as `add_bool_or`: `sum(literals) >= 1`."""
 951        return self._add_bool_argument_constraint(
 952            cmh.BoolArgumentConstraint.bool_or, *literals
 953        )
 954
 955    @overload
 956    def add_at_most_one(self, literals: Iterable[LiteralT]) -> Constraint: ...
 957
 958    @overload
 959    def add_at_most_one(self, *literals: LiteralT) -> Constraint: ...
 960
 961    def add_at_most_one(self, *literals) -> Constraint:
 962        """Adds `AtMostOne(literals)`: `sum(literals) <= 1`."""
 963        return self._add_bool_argument_constraint(
 964            cmh.BoolArgumentConstraint.at_most_one, *literals
 965        )
 966
 967    @overload
 968    def add_exactly_one(self, literals: Iterable[LiteralT]) -> Constraint: ...
 969
 970    @overload
 971    def add_exactly_one(self, *literals: LiteralT) -> Constraint: ...
 972
 973    def add_exactly_one(self, *literals):
 974        """Adds `ExactlyOne(literals)`: `sum(literals) == 1`."""
 975        return self._add_bool_argument_constraint(
 976            cmh.BoolArgumentConstraint.exactly_one, *literals
 977        )
 978
 979    @overload
 980    def add_bool_and(self, literals: Iterable[LiteralT]) -> Constraint: ...
 981
 982    @overload
 983    def add_bool_and(self, *literals: LiteralT) -> Constraint: ...
 984
 985    def add_bool_and(self, *literals):
 986        """Adds `And(literals) == true`."""
 987        return self._add_bool_argument_constraint(
 988            cmh.BoolArgumentConstraint.bool_and, *literals
 989        )
 990
 991    @overload
 992    def add_bool_xor(self, literals: Iterable[LiteralT]) -> Constraint: ...
 993
 994    @overload
 995    def add_bool_xor(self, *literals: LiteralT) -> Constraint: ...
 996
 997    def add_bool_xor(self, *literals):
 998        """Adds `XOr(literals) == true`.
 999
1000        In contrast to add_bool_or and add_bool_and, it does not support
1001            .only_enforce_if().
1002
1003        Args:
1004          *literals: the list of literals in the constraint.
1005
1006        Returns:
1007          An `Constraint` object.
1008        """
1009        return self._add_bool_argument_constraint(
1010            cmh.BoolArgumentConstraint.bool_xor, *literals
1011        )
1012
1013    @overload
1014    def add_min_equality(
1015        self, target: LinearExprT, expressions: Iterable[LinearExprT]
1016    ) -> Constraint: ...
1017
1018    @overload
1019    def add_min_equality(
1020        self, target: LinearExprT, *expressions: LinearExprT
1021    ) -> Constraint: ...
1022
1023    def add_min_equality(self, target, *expressions) -> Constraint:
1024        """Adds `target == Min(expressions)`."""
1025        return self._add_linear_argument_constraint(
1026            cmh.LinearArgumentConstraint.min, target, *expressions
1027        )
1028
1029    @overload
1030    def add_max_equality(
1031        self, target: LinearExprT, expressions: Iterable[LinearExprT]
1032    ) -> Constraint: ...
1033
1034    @overload
1035    def add_max_equality(
1036        self, target: LinearExprT, *expressions: LinearExprT
1037    ) -> Constraint: ...
1038
1039    def add_max_equality(self, target, *expressions) -> Constraint:
1040        """Adds `target == Max(expressions)`."""
1041        return self._add_linear_argument_constraint(
1042            cmh.LinearArgumentConstraint.max, target, *expressions
1043        )
1044
1045    def add_division_equality(
1046        self, target: LinearExprT, num: LinearExprT, denom: LinearExprT
1047    ) -> Constraint:
1048        """Adds `target == num // denom` (integer division rounded towards 0)."""
1049        return self._add_linear_argument_constraint(
1050            cmh.LinearArgumentConstraint.div, target, [num, denom]
1051        )
1052
1053    def add_abs_equality(self, target: LinearExprT, expr: LinearExprT) -> Constraint:
1054        """Adds `target == Abs(expr)`."""
1055        return self._add_linear_argument_constraint(
1056            cmh.LinearArgumentConstraint.max, target, [expr, -expr]
1057        )
1058
1059    def add_modulo_equality(
1060        self, target: LinearExprT, expr: LinearExprT, mod: LinearExprT
1061    ) -> Constraint:
1062        """Adds `target = expr % mod`.
1063
1064        It uses the C convention, that is the result is the remainder of the
1065        integral division rounded towards 0.
1066
1067            For example:
1068            * 10 % 3 = 1
1069            * -10 % 3 = -1
1070            * 10 % -3 = 1
1071            * -10 % -3 = -1
1072
1073        Args:
1074          target: the target expression.
1075          expr: the expression to compute the modulo of.
1076          mod: the modulus expression.
1077
1078        Returns:
1079          A `Constraint` object.
1080        """
1081        return self._add_linear_argument_constraint(
1082            cmh.LinearArgumentConstraint.mod, target, [expr, mod]
1083        )
1084
1085    def add_multiplication_equality(
1086        self,
1087        target: LinearExprT,
1088        *expressions: Union[Iterable[LinearExprT], LinearExprT],
1089    ) -> Constraint:
1090        """Adds `target == expressions[0] * .. * expressions[n]`."""
1091        return self._add_linear_argument_constraint(
1092            cmh.LinearArgumentConstraint.prod, target, *expressions
1093        )
1094
1095    # Scheduling support
1096
1097    def new_interval_var(
1098        self, start: LinearExprT, size: LinearExprT, end: LinearExprT, name: str
1099    ) -> IntervalVar:
1100        """Creates an interval variable from start, size, and end.
1101
1102        An interval variable is a constraint, that is itself used in other
1103        constraints like NoOverlap.
1104
1105        Internally, it ensures that `start + size == end`.
1106
1107        Args:
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.
1112
1113        Returns:
1114          An `IntervalVar` object.
1115        """
1116        return self._new_interval_var(name, start, size, end, [])
1117
1118    def new_interval_var_series(
1119        self,
1120        name: str,
1121        index: pd.Index,
1122        starts: Union[LinearExprT, pd.Series],
1123        sizes: Union[LinearExprT, pd.Series],
1124        ends: Union[LinearExprT, pd.Series],
1125    ) -> pd.Series:
1126        """Creates a series of interval variables with the given name.
1127
1128        Args:
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.
1140
1141        Returns:
1142          pd.Series: The interval variable set indexed by its corresponding
1143          dimensions.
1144
1145        Raises:
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.
1149        """
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")
1154
1155        starts = _convert_to_series_and_validate_index(starts, index)
1156        sizes = _convert_to_series_and_validate_index(sizes, index)
1157        ends = _convert_to_series_and_validate_index(ends, index)
1158        interval_array = []
1159        for i in index:
1160            interval_array.append(
1161                self.new_interval_var(
1162                    start=starts[i],
1163                    size=sizes[i],
1164                    end=ends[i],
1165                    name=f"{name}[{i}]",
1166                )
1167            )
1168        return pd.Series(index=index, data=interval_array)
1169
1170    def new_fixed_size_interval_var(
1171        self, start: LinearExprT, size: IntegralT, name: str
1172    ) -> IntervalVar:
1173        """Creates an interval variable from start, and a fixed size.
1174
1175        An interval variable is a constraint, that is itself used in other
1176        constraints like NoOverlap.
1177
1178        Args:
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.
1182
1183        Returns:
1184          An `IntervalVar` object.
1185        """
1186        return self._new_interval_var(name, start, size, start + size, [])
1187
1188    def new_fixed_size_interval_var_series(
1189        self,
1190        name: str,
1191        index: pd.Index,
1192        starts: Union[LinearExprT, pd.Series],
1193        sizes: Union[IntegralT, pd.Series],
1194    ) -> pd.Series:
1195        """Creates a series of interval variables with the given name.
1196
1197        Args:
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.
1206
1207        Returns:
1208          pd.Series: The interval variable set indexed by its corresponding
1209          dimensions.
1210
1211        Raises:
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.
1215        """
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")
1220
1221        starts = _convert_to_series_and_validate_index(starts, index)
1222        sizes = _convert_to_series_and_validate_index(sizes, index)
1223        interval_array = []
1224        for i in index:
1225            interval_array.append(
1226                self.new_fixed_size_interval_var(
1227                    start=starts[i],
1228                    size=sizes[i],
1229                    name=f"{name}[{i}]",
1230                )
1231            )
1232        return pd.Series(index=index, data=interval_array)
1233
1234    def new_optional_interval_var(
1235        self,
1236        start: LinearExprT,
1237        size: LinearExprT,
1238        end: LinearExprT,
1239        is_present: LiteralT,
1240        name: str,
1241    ) -> IntervalVar:
1242        """Creates an optional interval var from start, size, end, and is_present.
1243
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.
1247
1248        Internally, it ensures that `is_present` implies `start + size ==
1249        end`.
1250
1251        Args:
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.
1258
1259        Returns:
1260          An `IntervalVar` object.
1261        """
1262        return self._new_interval_var(
1263            name,
1264            start,
1265            size,
1266            end,
1267            [is_present],
1268        )
1269
1270    def new_optional_interval_var_series(
1271        self,
1272        name: str,
1273        index: pd.Index,
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],
1278    ) -> pd.Series:
1279        """Creates a series of interval variables with the given name.
1280
1281        Args:
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.
1296
1297        Returns:
1298          pd.Series: The interval variable set indexed by its corresponding
1299          dimensions.
1300
1301        Raises:
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.
1305        """
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")
1310
1311        starts = _convert_to_series_and_validate_index(starts, index)
1312        sizes = _convert_to_series_and_validate_index(sizes, index)
1313        ends = _convert_to_series_and_validate_index(ends, index)
1314        are_present = _convert_to_series_and_validate_index(are_present, index)
1315
1316        interval_array = []
1317        for i in index:
1318            interval_array.append(
1319                self.new_optional_interval_var(
1320                    start=starts[i],
1321                    size=sizes[i],
1322                    end=ends[i],
1323                    is_present=are_present[i],
1324                    name=f"{name}[{i}]",
1325                )
1326            )
1327        return pd.Series(index=index, data=interval_array)
1328
1329    def new_optional_fixed_size_interval_var(
1330        self,
1331        start: LinearExprT,
1332        size: IntegralT,
1333        is_present: LiteralT,
1334        name: str,
1335    ) -> IntervalVar:
1336        """Creates an interval variable from start, and a fixed size.
1337
1338        An interval variable is a constraint, that is itself used in other
1339        constraints like NoOverlap.
1340
1341        Args:
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.
1347
1348        Returns:
1349          An `IntervalVar` object.
1350        """
1351        return self._new_interval_var(
1352            name,
1353            start,
1354            size,
1355            start + size,
1356            [is_present],
1357        )
1358
1359    def new_optional_fixed_size_interval_var_series(
1360        self,
1361        name: str,
1362        index: pd.Index,
1363        starts: Union[LinearExprT, pd.Series],
1364        sizes: Union[IntegralT, pd.Series],
1365        are_present: Union[LiteralT, pd.Series],
1366    ) -> pd.Series:
1367        """Creates a series of interval variables with the given name.
1368
1369        Args:
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.
1381
1382        Returns:
1383          pd.Series: The interval variable set indexed by its corresponding
1384          dimensions.
1385
1386        Raises:
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.
1390        """
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")
1395
1396        starts = _convert_to_series_and_validate_index(starts, index)
1397        sizes = _convert_to_series_and_validate_index(sizes, index)
1398        are_present = _convert_to_series_and_validate_index(are_present, index)
1399        interval_array = []
1400        for i in index:
1401            interval_array.append(
1402                self.new_optional_fixed_size_interval_var(
1403                    start=starts[i],
1404                    size=sizes[i],
1405                    is_present=are_present[i],
1406                    name=f"{name}[{i}]",
1407                )
1408            )
1409        return pd.Series(index=index, data=interval_array)
1410
1411    def add_no_overlap(self, intervals: Iterable[IntervalVar]) -> Constraint:
1412        """Adds NoOverlap(interval_vars).
1413
1414        A NoOverlap constraint ensures that all present intervals do not overlap
1415        in time.
1416
1417        Args:
1418          intervals: The list of interval variables to constrain.
1419
1420        Returns:
1421          An instance of the `Constraint` class.
1422        """
1423        return self._add_no_overlap(intervals)
1424
1425    def add_no_overlap_2d(
1426        self,
1427        x_intervals: Iterable[IntervalVar],
1428        y_intervals: Iterable[IntervalVar],
1429    ) -> Constraint:
1430        """Adds NoOverlap2D(x_intervals, y_intervals).
1431
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.
1435
1436        Furthermore, one box is optional if at least one of the x or y interval is
1437        optional.
1438
1439        Args:
1440          x_intervals: The X coordinates of the rectangles.
1441          y_intervals: The Y coordinates of the rectangles.
1442
1443        Returns:
1444          An instance of the `Constraint` class.
1445        """
1446        return self._add_no_overlap_2d(x_intervals, y_intervals)
1447
1448    def add_cumulative(
1449        self,
1450        intervals: Iterable[IntervalVar],
1451        demands: Iterable[LinearExprT],
1452        capacity: LinearExprT,
1453    ) -> Constraint:
1454        """Adds Cumulative(intervals, demands, capacity).
1455
1456        This constraint enforces that:
1457
1458            for all t:
1459              sum(demands[i]
1460                if (start(intervals[i]) <= t < end(intervals[i])) and
1461                (intervals[i] is present)) <= capacity
1462
1463        Args:
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).
1469
1470        Returns:
1471          An instance of the `Constraint` class.
1472        """
1473        return self._add_cumulative(intervals, demands, capacity)
1474
1475    # Support for model cloning.
1476    def clone(self) -> "CpModel":
1477        """Reset the model, and creates a new one from a CpModelProto instance."""
1478        clone = CpModel()
1479        clone.proto.copy_from(self.proto)
1480        clone.rebuild_constant_map()
1481        return clone
1482
1483    def __copy__(self):
1484        return CpModel(self.model_proto)
1485
1486    def __deepcopy__(self, memo):
1487        return CpModel(copy.deepcopy(self.model_proto, memo))
1488
1489    def get_bool_var_from_proto_index(self, index: int) -> IntVar:
1490        """Returns an already created Boolean variable from its index."""
1491        if index < 0 or index >= len(self.model_proto.variables):
1492            raise ValueError(
1493                f"get_bool_var_from_proto_index: out of bound index {index}"
1494            )
1495        result = IntVar(self.model_proto, index)
1496        if not result.is_boolean:
1497            raise TypeError(
1498                f"get_bool_var_from_proto_index: index {index} does not reference a"
1499                " boolean variable"
1500            )
1501        return result
1502
1503    def get_int_var_from_proto_index(self, index: int) -> IntVar:
1504        """Returns an already created integer variable from its index."""
1505        if index < 0 or index >= len(self.model_proto.variables):
1506            raise ValueError(
1507                f"get_int_var_from_proto_index: out of bound index {index}"
1508            )
1509        return IntVar(self.model_proto, index)
1510
1511    def get_interval_var_from_proto_index(self, index: int) -> IntervalVar:
1512        """Returns an already created interval variable from its index."""
1513        if index < 0 or index >= len(self.model_proto.constraints):
1514            raise ValueError(
1515                f"get_interval_var_from_proto_index: out of bound index {index}"
1516            )
1517        ct = self.model_proto.constraints[index]
1518        if not ct.has_interval():
1519            raise ValueError(
1520                f"get_interval_var_from_proto_index: index {index} does not"
1521                " reference an" + " interval variable"
1522            )
1523
1524        return IntervalVar(self.model_proto, index)
1525
1526    def __str__(self) -> str:
1527        return str(self.model_proto)
1528
1529    @property
1530    def proto(self) -> cmh.CpModelProto:
1531        """Returns the underlying CpModelProto."""
1532        return self.model_proto
1533
1534    def negated(self, index: int) -> int:
1535        return -index - 1
1536
1537    def _set_objective(self, obj: ObjLinearExprT, maximize: bool):
1538        """Sets the objective of the model."""
1539        self.clear_objective()
1540        if isinstance(obj, IntegralTypes):
1541            self.model_proto.objective.offset = int(obj)
1542            self.model_proto.objective.scaling_factor = 1.0
1543        elif isinstance(obj, LinearExpr):
1544            if obj.is_integer():
1545                int_obj = cmh.FlatIntExpr(obj)
1546                for var in int_obj.vars:
1547                    self.model_proto.objective.vars.append(var.index)
1548                if maximize:
1549                    self.model_proto.objective.scaling_factor = -1.0
1550                    self.model_proto.objective.offset = -int_obj.offset
1551                    for c in int_obj.coeffs:
1552                        self.model_proto.objective.coeffs.append(-c)
1553                else:
1554                    self.model_proto.objective.scaling_factor = 1.0
1555                    self.model_proto.objective.offset = int_obj.offset
1556                    self.model_proto.objective.coeffs.extend(int_obj.coeffs)
1557            else:
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(
1562                    float_obj.coeffs
1563                )
1564                self.model_proto.floating_point_objective.maximize = maximize
1565                self.model_proto.floating_point_objective.offset = float_obj.offset
1566        else:
1567            raise TypeError(
1568                f"TypeError: {type(obj).__name__!r} is not a valid objective"
1569            )
1570
1571    def minimize(self, obj: ObjLinearExprT):
1572        """Sets the objective of the model to minimize(obj)."""
1573        self._set_objective(obj, maximize=False)
1574
1575    def maximize(self, obj: ObjLinearExprT):
1576        """Sets the objective of the model to maximize(obj)."""
1577        self._set_objective(obj, maximize=True)
1578
1579    def has_objective(self) -> bool:
1580        return (
1581            self.model_proto.has_objective()
1582            or self.model_proto.has_floating_point_objective()
1583        )
1584
1585    def clear_objective(self):
1586        self.model_proto.clear_objective()
1587        self.model_proto.clear_floating_point_objective()
1588
1589    def add_decision_strategy(
1590        self,
1591        variables: Iterable[IntVar],
1592        var_strategy: cmh.DecisionStrategyProto.VariableSelectionStrategy,
1593        domain_strategy: cmh.DecisionStrategyProto.DomainReductionStrategy,
1594    ) -> None:
1595        """Adds a search strategy to the model.
1596
1597        Args:
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,
1603            solve() will fail.
1604        """
1605
1606        strategy: cmh.DecisionStrategyProto = self.model_proto.search_strategy.add()
1607        for v in variables:
1608            expr = strategy.exprs.add()
1609            if v.index >= 0:
1610                expr.vars.append(v.index)
1611                expr.coeffs.append(1)
1612            else:
1613                expr.vars.append(self.negated(v.index))
1614                expr.coeffs.append(-1)
1615                expr.offset = 1
1616
1617        strategy.variable_selection_strategy = var_strategy
1618        strategy.domain_reduction_strategy = domain_strategy
1619
1620    def model_stats(self) -> str:
1621        """Returns a string containing some model statistics."""
1622        return cmh.CpSatHelper.model_stats(self.model_proto)
1623
1624    def validate(self) -> str:
1625        """Returns a string indicating that the model is invalid."""
1626        return cmh.CpSatHelper.validate_model(self.model_proto)
1627
1628    def export_to_file(self, file: str) -> bool:
1629        """Write the model as a protocol buffer to 'file'.
1630
1631        Args:
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
1634            be used.
1635
1636        Returns:
1637          True if the model was correctly written.
1638        """
1639        return cmh.CpSatHelper.write_model_to_file(self.model_proto, file)
1640
1641    def remove_all_names(self) -> None:
1642        """Removes all names from the model."""
1643        self.model_proto.clear_name()
1644        for v in self.model_proto.variables:
1645            v.clear_name()
1646        for c in self.model_proto.constraints:
1647            c.clear_name()
1648
1649    @overload
1650    def add_hint(self, var: IntVar, value: int) -> None: ...
1651
1652    @overload
1653    def add_hint(self, literal: BoolVarT, value: bool) -> None: ...
1654
1655    def add_hint(self, var, value) -> None:
1656        """Adds 'var == value' as a hint to the solver."""
1657        if var.index >= 0:
1658            self.model_proto.solution_hint.vars.append(var.index)
1659            self.model_proto.solution_hint.values.append(int(value))
1660        else:
1661            self.model_proto.solution_hint.vars.append(self.negated(var.index))
1662            self.model_proto.solution_hint.values.append(int(not value))
1663
1664    def clear_hints(self):
1665        """Removes any solution hint from the model."""
1666        self.model_proto.clear_solution_hint()
1667
1668    def add_assumption(self, lit: LiteralT) -> None:
1669        """Adds the literal to the model as assumptions."""
1670        self.model_proto.assumptions.append(self.get_or_make_boolean_index(lit))
1671
1672    def add_assumptions(self, literals: Iterable[LiteralT]) -> None:
1673        """Adds the literals to the model as assumptions."""
1674        for lit in literals:
1675            self.add_assumption(lit)
1676
1677    def clear_assumptions(self) -> None:
1678        """Removes all assumptions from the model."""
1679        self.model_proto.assumptions.clear()
1680
1681    # Compatibility with pre PEP8
1682    # pylint: disable=invalid-name
1683
1684    def _add_pre_pep8_methods(self) -> None:
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_")
1690            ):
1691                pre_pep8_name = snake_case_to_camel_case(method_name)
1692                setattr(
1693                    self,
1694                    pre_pep8_name,
1695                    deprecated_method(getattr(self, method_name), pre_pep8_name),
1696                )
1697
1698        for other_method_name in [
1699            "add",
1700            "clone",
1701            "get_bool_var_from_proto_index",
1702            "get_int_var_from_proto_index",
1703            "get_interval_var_from_proto_index",
1704            "minimize",
1705            "maximize",
1706            "has_objective",
1707            "model_stats",
1708            "validate",
1709            "export_to_file",
1710        ]:
1711            pre_pep8_name = snake_case_to_camel_case(other_method_name)
1712            setattr(
1713                self,
1714                pre_pep8_name,
1715                deprecated_method(getattr(self, other_method_name), pre_pep8_name),
1716            )
1717
1718    @deprecated("Use name property instead.")
1719    def Name(self) -> str:
1720        return self.name
1721
1722    @deprecated("Use name property instead.")
1723    def SetName(self, name: str) -> None:
1724        self.name = name
1725
1726    @deprecated("Use proto property instead.")
1727    def Proto(self) -> cmh.CpModelProto:
1728        return self.proto
1729
1730    # pylint: enable=invalid-name
1731
1732
1733class CpSolver:
1734    """Main solver class.
1735
1736    The purpose of this class is to search for a solution to the model provided
1737    to the solve() method.
1738
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.
1742    """
1743
1744    def __init__(self) -> None:
1745        self.__response: Optional[cmh.CpSolverResponse] = None
1746        self.parameters: cmh.SatParameters = cmh.SatParameters()
1747        self.log_callback: Optional[Callable[[str], None]] = None
1748        self.best_bound_callback: Optional[Callable[[float], None]] = None
1749        self.__solve_wrapper: Optional[cmh.SolveWrapper] = None
1750        self.__lock: threading.Lock = threading.Lock()
1751
1752    def solve(
1753        self,
1754        model: CpModel,
1755        solution_callback: Optional["CpSolverSolutionCallback"] = None,
1756    ) -> cmh.CpSolverStatus:
1757        """Solves a problem and passes each solution to the callback if not null."""
1758        with self.__lock:
1759            self.__solve_wrapper = cmh.SolveWrapper()
1760
1761        self.__solve_wrapper.set_parameters(self.parameters)
1762        if solution_callback is not None:
1763            self.__solve_wrapper.add_solution_callback(solution_callback)
1764
1765        if self.log_callback is not None:
1766            self.__solve_wrapper.add_log_callback(self.log_callback)
1767
1768        if self.best_bound_callback is not None:
1769            self.__solve_wrapper.add_best_bound_callback(self.best_bound_callback)
1770
1771        self.__response = self.__solve_wrapper.solve(model.proto)
1772
1773        if solution_callback is not None:
1774            self.__solve_wrapper.clear_solution_callback(solution_callback)
1775
1776        with self.__lock:
1777            self.__solve_wrapper = None
1778
1779        return self.__response.status
1780
1781    def stop_search(self) -> None:
1782        """Stops the current search asynchronously."""
1783        with self.__lock:
1784            if self.__solve_wrapper:
1785                self.__solve_wrapper.stop_search()
1786
1787    def value(self, expression: LinearExprT) -> int:
1788        """Returns the value of a linear expression after solve."""
1789        return cmh.ResponseHelper.value(self._checked_response, expression)
1790
1791    def values(self, variables: _IndexOrSeries) -> pd.Series:
1792        """Returns the values of the input variables.
1793
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
1797        dimensions.
1798
1799        Args:
1800          variables (Union[pd.Index, pd.Series]): The set of variables from which to
1801            get the values.
1802
1803        Returns:
1804          pd.Series: The values of all variables in the set.
1805
1806        Raises:
1807          RuntimeError: if solve() has not been called.
1808        """
1809        response: cmh.CpSolverResponse = self._checked_response
1810        return pd.Series(
1811            data=[cmh.ResponseHelper.value(response, var) for var in variables],
1812            index=_get_index(variables),
1813        )
1814
1815    def float_value(self, expression: LinearExprT) -> float:
1816        """Returns the value of a linear expression after solve."""
1817        return cmh.ResponseHelper.float_value(self._checked_response, expression)
1818
1819    def float_values(self, expressions: _IndexOrSeries) -> pd.Series:
1820        """Returns the float values of the input linear expressions.
1821
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
1825        dimensions.
1826
1827        Args:
1828          expressions (Union[pd.Index, pd.Series]): The set of expressions from
1829            which to get the values.
1830
1831        Returns:
1832          pd.Series: The values of all variables in the set.
1833
1834        Raises:
1835          RuntimeError: if solve() has not been called.
1836        """
1837        response: cmh.CpSolverResponse = self._checked_response
1838        return pd.Series(
1839            data=[
1840                cmh.ResponseHelper.float_value(response, expr) for expr in expressions
1841            ],
1842            index=_get_index(expressions),
1843        )
1844
1845    def boolean_value(self, literal: LiteralT) -> bool:
1846        """Returns the boolean value of a literal after solve."""
1847        return cmh.ResponseHelper.boolean_value(self._checked_response, literal)
1848
1849    def boolean_values(self, variables: _IndexOrSeries) -> pd.Series:
1850        """Returns the values of the input variables.
1851
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
1855        dimensions.
1856
1857        Args:
1858          variables (Union[pd.Index, pd.Series]): The set of variables from which to
1859            get the values.
1860
1861        Returns:
1862          pd.Series: The values of all variables in the set.
1863
1864        Raises:
1865          RuntimeError: if solve() has not been called.
1866        """
1867        response: cmh.CpSolverResponse = self._checked_response
1868        return pd.Series(
1869            data=[
1870                cmh.ResponseHelper.boolean_value(response, literal)
1871                for literal in variables
1872            ],
1873            index=_get_index(variables),
1874        )
1875
1876    @property
1877    def objective_value(self) -> float:
1878        """Returns the value of the objective after solve."""
1879        return self._checked_response.objective_value
1880
1881    @property
1882    def best_objective_bound(self) -> float:
1883        """Returns the best lower (upper) bound found when min(max)imizing."""
1884        return self._checked_response.best_objective_bound
1885
1886    @property
1887    def num_booleans(self) -> int:
1888        """Returns the number of boolean variables managed by the SAT solver."""
1889        return self._checked_response.num_booleans
1890
1891    @property
1892    def num_conflicts(self) -> int:
1893        """Returns the number of conflicts since the creation of the solver."""
1894        return self._checked_response.num_conflicts
1895
1896    @property
1897    def num_branches(self) -> int:
1898        """Returns the number of search branches explored by the solver."""
1899        return self._checked_response.num_branches
1900
1901    @property
1902    def num_binary_propagations(self) -> int:
1903        """Returns the number of Boolean propagations done by the solver."""
1904        return self._checked_response.num_binary_propagations
1905
1906    @property
1907    def num_integer_propagations(self) -> int:
1908        """Returns the number of integer propagations done by the solver."""
1909        return self._checked_response.num_integer_propagations
1910
1911    @property
1912    def deterministic_time(self) -> float:
1913        """Returns the deterministic time in seconds since the creation of the solver."""
1914        return self._checked_response.deterministic_time
1915
1916    @property
1917    def wall_time(self) -> float:
1918        """Returns the wall time in seconds since the creation of the solver."""
1919        return self._checked_response.wall_time
1920
1921    @property
1922    def user_time(self) -> float:
1923        """Returns the user time in seconds since the creation of the solver."""
1924        return self._checked_response.user_time
1925
1926    @property
1927    def solve_log(self) -> str:
1928        """Returns the solve log.
1929
1930        To enable this, the parameter log_to_response must be set to True.
1931        """
1932        return self._checked_response.solve_log
1933
1934    @property
1935    def solve_info(self) -> str:
1936        """Returns the information about the solve."""
1937        return self._checked_response.solve_info
1938
1939    @property
1940    def response_proto(self) -> cmh.CpSolverResponse:
1941        """Returns the response object."""
1942        return self._checked_response
1943
1944    def response_stats(self) -> str:
1945        """Returns some statistics on the solution found as a string."""
1946        return cmh.CpSatHelper.solver_response_stats(self._checked_response)
1947
1948    def sufficient_assumptions_for_infeasibility(self) -> Sequence[int]:
1949        """Returns the indices of the infeasible assumptions."""
1950        return cmh.ResponseHelper.sufficient_assumptions_for_infeasibility(
1951            self._checked_response
1952        )
1953
1954    def status_name(self, status: Optional[Any] = None) -> str:
1955        """Returns the name of the status returned by solve()."""
1956        if status is None:
1957            status = self._checked_response.status()
1958        return status.name
1959
1960    def solution_info(self) -> str:
1961        """Returns some information on the solve process.
1962
1963        Returns some information on how the solution was found, or the reason
1964        why the model or the parameters are invalid.
1965
1966        Raises:
1967          RuntimeError: if solve() has not been called.
1968        """
1969        return self._checked_response.solution_info
1970
1971    @property
1972    def _checked_response(self) -> cmh.CpSolverResponse:
1973        """Checks solve() has been called, and returns a response wrapper."""
1974        if self.__response is None:
1975            raise RuntimeError("solve() has not been called.")
1976        return self.__response
1977
1978    # Compatibility with pre PEP8
1979    # pylint: disable=invalid-name
1980
1981    @deprecated("Use best_objective_bound property instead.")
1982    def BestObjectiveBound(self) -> float:
1983        return self.best_objective_bound
1984
1985    @deprecated("Use boolean_value() method instead.")
1986    def BooleanValue(self, lit: LiteralT) -> bool:
1987        return self.boolean_value(lit)
1988
1989    @deprecated("Use boolean_values() method instead.")
1990    def BooleanValues(self, variables: _IndexOrSeries) -> pd.Series:
1991        return self.boolean_values(variables)
1992
1993    @deprecated("Use num_booleans property instead.")
1994    def NumBooleans(self) -> int:
1995        return self.num_booleans
1996
1997    @deprecated("Use num_conflicts property instead.")
1998    def NumConflicts(self) -> int:
1999        return self.num_conflicts
2000
2001    @deprecated("Use num_branches property instead.")
2002    def NumBranches(self) -> int:
2003        return self.num_branches
2004
2005    @deprecated("Use objective_value property instead.")
2006    def ObjectiveValue(self) -> float:
2007        return self.objective_value
2008
2009    @deprecated("Use response_proto property instead.")
2010    def ResponseProto(self) -> cmh.CpSolverResponse:
2011        return self.response_proto
2012
2013    @deprecated("Use response_stats() method instead.")
2014    def ResponseStats(self) -> str:
2015        return self.response_stats()
2016
2017    @deprecated("Use solve() method instead.")
2018    def Solve(
2019        self, model: CpModel, callback: "CpSolverSolutionCallback" = None
2020    ) -> cmh.CpSolverStatus:
2021        return self.solve(model, callback)
2022
2023    @deprecated("Use solution_info() method instead.")
2024    def SolutionInfo(self) -> str:
2025        return self.solution_info()
2026
2027    @deprecated("Use status_name() method instead.")
2028    def StatusName(self, status: Optional[Any] = None) -> str:
2029        return self.status_name(status)
2030
2031    @deprecated("Use stop_search() method instead.")
2032    def StopSearch(self) -> None:
2033        self.stop_search()
2034
2035    @deprecated("Use sufficient_assumptions_for_infeasibility() method instead.")
2036    def SufficientAssumptionsForInfeasibility(self) -> Sequence[int]:
2037        return self.sufficient_assumptions_for_infeasibility()
2038
2039    @deprecated("Use user_time property instead.")
2040    def UserTime(self) -> float:
2041        return self.user_time
2042
2043    @deprecated("Use value() method instead.")
2044    def Value(self, expression: LinearExprT) -> int:
2045        return self.value(expression)
2046
2047    @deprecated("Use values() method instead.")
2048    def Values(self, expressions: _IndexOrSeries) -> pd.Series:
2049        return self.values(expressions)
2050
2051    @deprecated("Use wall_time property instead.")
2052    def WallTime(self) -> float:
2053        return self.wall_time
2054
2055    @deprecated("Use solve() with enumerate_all_solutions = True.")
2056    def SearchForAllSolutions(
2057        self, model: CpModel, callback: "CpSolverSolutionCallback"
2058    ) -> cmh.CpSolverStatus:
2059        """Search for all solutions of a satisfiability problem.
2060
2061        This method searches for all feasible solutions of a given model.
2062        Then it feeds the solution to the callback.
2063
2064        Note that the model cannot contain an objective.
2065
2066        Args:
2067          model: The model to solve.
2068          callback: The callback that will be called at each solution.
2069
2070        Returns:
2071          The status of the solve:
2072
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
2076        """
2077        if model.has_objective():
2078            raise TypeError(
2079                "Search for all solutions is only defined on satisfiability problems"
2080            )
2081        # Store old parameter.
2082        enumerate_all = self.parameters.enumerate_all_solutions
2083        self.parameters.enumerate_all_solutions = True
2084
2085        status: cmh.CpSolverStatus = self.solve(model, callback)
2086
2087        # Restore parameter.
2088        self.parameters.enumerate_all_solutions = enumerate_all
2089        return status
2090
2091
2092# pylint: enable=invalid-name
2093
2094
2095class CpSolverSolutionCallback(cmh.SolutionCallback):
2096    """Solution callback.
2097
2098    This class implements a callback that will be called at each new solution
2099    found during search.
2100
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.
2104
2105    These methods returns the same information as their counterpart in the
2106    `CpSolver` class.
2107    """
2108
2109    def __init__(self) -> None:
2110        cmh.SolutionCallback.__init__(self)
2111
2112    # pylint: disable=invalid-name
2113    def OnSolutionCallback(self) -> None:
2114        """Proxy for the same method in snake case."""
2115        self.on_solution_callback()
2116
2117    # pylint: enable=invalid-name
2118
2119    def boolean_value(self, lit: LiteralT) -> bool:
2120        """Returns the boolean value of a boolean literal.
2121
2122        Args:
2123            lit: A boolean variable or its negation.
2124
2125        Returns:
2126            The Boolean value of the literal in the solution.
2127
2128        Raises:
2129            RuntimeError: if `lit` is not a boolean variable or its negation.
2130        """
2131        if not self.has_response():
2132            raise RuntimeError("solve() has not been called.")
2133        return self.BooleanValue(lit)
2134
2135    def value(self, expression: LinearExprT) -> int:
2136        """Evaluates an linear expression in the current solution.
2137
2138        Args:
2139            expression: a linear expression of the model.
2140
2141        Returns:
2142            An integer value equal to the evaluation of the linear expression
2143            against the current solution.
2144
2145        Raises:
2146            RuntimeError: if 'expression' is not a LinearExpr.
2147        """
2148        if not self.has_response():
2149            raise RuntimeError("solve() has not been called.")
2150        return self.Value(expression)
2151
2152    def float_value(self, expression: LinearExprT) -> float:
2153        """Evaluates an linear expression in the current solution.
2154
2155        Args:
2156            expression: a linear expression of the model.
2157
2158        Returns:
2159            An integer value equal to the evaluation of the linear expression
2160            against the current solution.
2161
2162        Raises:
2163            RuntimeError: if 'expression' is not a LinearExpr.
2164        """
2165        if not self.has_response():
2166            raise RuntimeError("solve() has not been called.")
2167        return self.FloatValue(expression)
2168
2169    def has_response(self) -> bool:
2170        return self.HasResponse()
2171
2172    def stop_search(self) -> None:
2173        """Stops the current search asynchronously."""
2174        if not self.has_response():
2175            raise RuntimeError("solve() has not been called.")
2176        self.StopSearch()
2177
2178    @property
2179    def objective_value(self) -> float:
2180        """Returns the value of the objective after solve."""
2181        if not self.has_response():
2182            raise RuntimeError("solve() has not been called.")
2183        return self.ObjectiveValue()
2184
2185    @property
2186    def best_objective_bound(self) -> float:
2187        """Returns the best lower (upper) bound found when min(max)imizing."""
2188        if not self.has_response():
2189            raise RuntimeError("solve() has not been called.")
2190        return self.BestObjectiveBound()
2191
2192    @property
2193    def num_booleans(self) -> int:
2194        """Returns the number of boolean variables managed by the SAT solver."""
2195        if not self.has_response():
2196            raise RuntimeError("solve() has not been called.")
2197        return self.NumBooleans()
2198
2199    @property
2200    def num_conflicts(self) -> int:
2201        """Returns the number of conflicts since the creation of the solver."""
2202        if not self.has_response():
2203            raise RuntimeError("solve() has not been called.")
2204        return self.NumConflicts()
2205
2206    @property
2207    def num_branches(self) -> int:
2208        """Returns the number of search branches explored by the solver."""
2209        if not self.has_response():
2210            raise RuntimeError("solve() has not been called.")
2211        return self.NumBranches()
2212
2213    @property
2214    def num_integer_propagations(self) -> int:
2215        """Returns the number of integer propagations done by the solver."""
2216        if not self.has_response():
2217            raise RuntimeError("solve() has not been called.")
2218        return self.NumIntegerPropagations()
2219
2220    @property
2221    def num_binary_propagations(self) -> int:
2222        """Returns the number of Boolean propagations done by the solver."""
2223        if not self.has_response():
2224            raise RuntimeError("solve() has not been called.")
2225        return self.NumBinaryPropagations()
2226
2227    @property
2228    def deterministic_time(self) -> float:
2229        """Returns the determistic time in seconds since the creation of the solver."""
2230        if not self.has_response():
2231            raise RuntimeError("solve() has not been called.")
2232        return self.DeterministicTime()
2233
2234    @property
2235    def wall_time(self) -> float:
2236        """Returns the wall time in seconds since the creation of the solver."""
2237        if not self.has_response():
2238            raise RuntimeError("solve() has not been called.")
2239        return self.WallTime()
2240
2241    @property
2242    def user_time(self) -> float:
2243        """Returns the user time in seconds since the creation of the solver."""
2244        if not self.has_response():
2245            raise RuntimeError("solve() has not been called.")
2246        return self.UserTime()
2247
2248    @property
2249    def response_proto(self) -> cmh.CpSolverResponse:
2250        """Returns the response object."""
2251        if not self.has_response():
2252            raise RuntimeError("solve() has not been called.")
2253        return self.Response()
2254
2255
2256class ObjectiveSolutionPrinter(CpSolverSolutionCallback):
2257    """Display the objective value and time of intermediate solutions."""
2258
2259    def __init__(self) -> None:
2260        CpSolverSolutionCallback.__init__(self)
2261        self.__solution_count = 0
2262        self.__start_time = time.time()
2263
2264    def on_solution_callback(self) -> None:
2265        """Called on each new solution."""
2266        current_time = time.time()
2267        obj = self.objective_value
2268        print(
2269            f"Solution {self.__solution_count}, time ="
2270            f" {current_time - self.__start_time:0.2f} s, objective = {obj}",
2271            flush=True,
2272        )
2273        self.__solution_count += 1
2274
2275    def solution_count(self) -> int:
2276        """Returns the number of solutions found."""
2277        return self.__solution_count
2278
2279
2280class VarArrayAndObjectiveSolutionPrinter(CpSolverSolutionCallback):
2281    """Print intermediate solutions (objective, variable values, time)."""
2282
2283    def __init__(self, variables: Sequence[IntVar]) -> None:
2284        CpSolverSolutionCallback.__init__(self)
2285        self.__variables: Sequence[IntVar] = variables
2286        self.__solution_count: int = 0
2287        self.__start_time: float = time.time()
2288
2289    def on_solution_callback(self) -> None:
2290        """Called on each new solution."""
2291        current_time = time.time()
2292        obj = self.objective_value
2293        print(
2294            f"Solution {self.__solution_count}, time ="
2295            f" {current_time - self.__start_time:0.2f} s, objective = {obj}"
2296        )
2297        for v in self.__variables:
2298            print(f"  {v} = {self.value(v)}", end=" ")
2299        print(flush=True)
2300        self.__solution_count += 1
2301
2302    @property
2303    def solution_count(self) -> int:
2304        """Returns the number of solutions found."""
2305        return self.__solution_count
2306
2307
2308class VarArraySolutionPrinter(CpSolverSolutionCallback):
2309    """Print intermediate solutions (variable values, time)."""
2310
2311    def __init__(self, variables: Sequence[IntVar]) -> None:
2312        CpSolverSolutionCallback.__init__(self)
2313        self.__variables: Sequence[IntVar] = variables
2314        self.__solution_count: int = 0
2315        self.__start_time: float = time.time()
2316
2317    def on_solution_callback(self) -> None:
2318        """Called on each new solution."""
2319        current_time = time.time()
2320        print(
2321            f"Solution {self.__solution_count}, time ="
2322            f" {current_time - self.__start_time:0.2f} s"
2323        )
2324        for v in self.__variables:
2325            print(f"  {v} = {self.value(v)}", end=" ")
2326        print(flush=True)
2327        self.__solution_count += 1
2328
2329    @property
2330    def solution_count(self) -> int:
2331        """Returns the number of solutions found."""
2332        return self.__solution_count
class BoundedLinearExpression(pybind11_builtins.pybind11_object):

A class to hold a linear expression with bounds.

class Constraint(pybind11_builtins.pybind11_object):

Base class for constraints.

Constraints are built by the CpModel through the add methods. Once created by the CpModel class, they are automatically added to the model. The purpose of this class is to allow specification of enforcement literals for this constraint.

b = model.new_bool_var('b')
x = model.new_int_var(0, 10, 'x')
y = model.new_int_var(0, 10, 'y')

model.add(x + 2 * y == 5).only_enforce_if(b.negated())
model_proto

(arg0: ortools.sat.python.cp_model_helper.Constraint) -> operations_research::sat::CpModelProto

proto

(arg0: ortools.sat.python.cp_model_helper.Constraint) -> operations_research::sat::ConstraintProto

def with_name(unknown):

with_name(self: ortools.sat.python.cp_model_helper.Constraint, arg0: str) -> ortools.sat.python.cp_model_helper.Constraint

Sets the name of the constraint and returns the constraints

def only_enforce_if(unknown):

only_enforce_if(args, *kwargs) Overloaded function.

  1. only_enforce_if(self: ortools.sat.python.cp_model_helper.Constraint, literal: ortools.sat.python.cp_model_helper.Literal) -> ortools.sat.python.cp_model_helper.Constraint

    Adds one or more enforcement literals to the constraint.

    This method adds one or more literals (that is, a boolean variable or its negation) as enforcement literals. The conjunction of all these literals determines whether the constraint is active or not. It acts as an implication, so if the conjunction is true, it implies that the constraint must be enforced. If it is false, then the constraint is ignored.

    BoolOr, BoolAnd, and linear constraints all support enforcement literals.

    Args: *literals: One or more Boolean literals.

    Returns: self.

  2. only_enforce_if(self: ortools.sat.python.cp_model_helper.Constraint, literal: bool) -> ortools.sat.python.cp_model_helper.Constraint

    Adds one or more enforcement literals to the constraint.

    This method adds one or more literals (that is, a boolean variable or its negation) as enforcement literals. The conjunction of all these literals determines whether the constraint is active or not. It acts as an implication, so if the conjunction is true, it implies that the constraint must be enforced. If it is false, then the constraint is ignored.

    BoolOr, BoolAnd, and linear constraints all support enforcement literals.

    Args: *literals: One or more Boolean literals.

    Returns: self.

  3. only_enforce_if(self: ortools.sat.python.cp_model_helper.Constraint, literals: list[ortools.sat.python.cp_model_helper.Literal]) -> None

    Adds one or more enforcement literals to the constraint.

    This method adds one or more literals (that is, a boolean variable or its negation) as enforcement literals. The conjunction of all these literals determines whether the constraint is active or not. It acts as an implication, so if the conjunction is true, it implies that the constraint must be enforced. If it is false, then the constraint is ignored.

    BoolOr, BoolAnd, and linear constraints all support enforcement literals.

    Args: *literals: One or more Boolean literals.

    Returns: self.

  4. only_enforce_if(self: ortools.sat.python.cp_model_helper.Constraint, *args) -> None

    Adds one or more enforcement literals to the constraint.

    This method adds one or more literals (that is, a boolean variable or its negation) as enforcement literals. The conjunction of all these literals determines whether the constraint is active or not. It acts as an implication, so if the conjunction is true, it implies that the constraint must be enforced. If it is false, then the constraint is ignored.

    BoolOr, BoolAnd, and linear constraints all support enforcement literals.

    Args: *literals: One or more Boolean literals.

    Returns: self.

def Name(unknown):
def Index(unknown):
def Proto(unknown):

Proto(self: ortools.sat.python.cp_model_helper.Constraint) -> operations_research::sat::ConstraintProto

def OnlyEnforceIf(unknown):

OnlyEnforceIf(self: ortools.sat.python.cp_model_helper.Constraint, *args) -> None

class CpModelProto(pybind11_builtins.pybind11_object):
CpModelProto()
def merge_text_format(unknown):

merge_text_format(self: ortools.sat.python.cp_model_helper.CpModelProto, arg0: str) -> bool

def parse_text_format(unknown):

parse_text_format(self: ortools.sat.python.cp_model_helper.CpModelProto, arg0: str) -> bool

def clear_name(unknown):
variables

(arg0: ortools.sat.python.cp_model_helper.CpModelProto) -> google::protobuf::RepeatedPtrField

constraints

(arg0: ortools.sat.python.cp_model_helper.CpModelProto) -> google::protobuf::RepeatedPtrField

def clear_objective(unknown):

clear_objective(self: ortools.sat.python.cp_model_helper.CpModelProto) -> None

def has_objective(unknown):
floating_point_objective

(arg0: ortools.sat.python.cp_model_helper.CpModelProto) -> operations_research::sat::FloatObjectiveProto

def clear_floating_point_objective(unknown):

clear_floating_point_objective(self: ortools.sat.python.cp_model_helper.CpModelProto) -> None

def has_floating_point_objective(unknown):

has_floating_point_objective(self: ortools.sat.python.cp_model_helper.CpModelProto) -> bool

search_strategy

(arg0: ortools.sat.python.cp_model_helper.CpModelProto) -> google::protobuf::RepeatedPtrField

solution_hint

(arg0: ortools.sat.python.cp_model_helper.CpModelProto) -> operations_research::sat::PartialVariableAssignment

def clear_solution_hint(unknown):

clear_solution_hint(self: ortools.sat.python.cp_model_helper.CpModelProto) -> None

def has_solution_hint(unknown):

has_solution_hint(self: ortools.sat.python.cp_model_helper.CpModelProto) -> bool

assumptions

(arg0: ortools.sat.python.cp_model_helper.CpModelProto) -> google::protobuf::RepeatedField

symmetry

(arg0: ortools.sat.python.cp_model_helper.CpModelProto) -> operations_research::sat::SymmetryProto

def clear_symmetry(unknown):
def has_symmetry(unknown):
class CpSolverResponse(pybind11_builtins.pybind11_object):
CpSolverResponse()
def merge_text_format(unknown):

merge_text_format(self: ortools.sat.python.cp_model_helper.CpSolverResponse, arg0: str) -> bool

def parse_text_format(unknown):

parse_text_format(self: ortools.sat.python.cp_model_helper.CpSolverResponse, arg0: str) -> bool

status

(arg0: ortools.sat.python.cp_model_helper.CpSolverResponse) -> operations_research::sat::CpSolverStatus

def clear_status(unknown):
solution

(arg0: ortools.sat.python.cp_model_helper.CpSolverResponse) -> google::protobuf::RepeatedField

objective_value
def clear_objective_value(unknown):

clear_objective_value(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

best_objective_bound
def clear_best_objective_bound(unknown):

clear_best_objective_bound(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

additional_solutions

(arg0: ortools.sat.python.cp_model_helper.CpSolverResponse) -> google::protobuf::RepeatedPtrField

tightened_variables

(arg0: ortools.sat.python.cp_model_helper.CpSolverResponse) -> google::protobuf::RepeatedPtrField

sufficient_assumptions_for_infeasibility

(arg0: ortools.sat.python.cp_model_helper.CpSolverResponse) -> google::protobuf::RepeatedField

integer_objective

(arg0: ortools.sat.python.cp_model_helper.CpSolverResponse) -> operations_research::sat::CpObjectiveProto

def clear_integer_objective(unknown):

clear_integer_objective(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

def has_integer_objective(unknown):

has_integer_objective(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> bool

inner_objective_lower_bound
def clear_inner_objective_lower_bound(unknown):

clear_inner_objective_lower_bound(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

def clear_num_integers(unknown):

clear_num_integers(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

def clear_num_booleans(unknown):

clear_num_booleans(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

num_fixed_booleans
def clear_num_fixed_booleans(unknown):

clear_num_fixed_booleans(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

def clear_num_conflicts(unknown):

clear_num_conflicts(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

def clear_num_branches(unknown):

clear_num_branches(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

num_binary_propagations
def clear_num_binary_propagations(unknown):

clear_num_binary_propagations(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

num_integer_propagations
def clear_num_integer_propagations(unknown):

clear_num_integer_propagations(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

def clear_num_restarts(unknown):

clear_num_restarts(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

num_lp_iterations
def clear_num_lp_iterations(unknown):

clear_num_lp_iterations(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

def clear_wall_time(unknown):
def clear_user_time(unknown):
deterministic_time
def clear_deterministic_time(unknown):

clear_deterministic_time(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

def clear_gap_integral(unknown):

clear_gap_integral(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

def clear_solution_info(unknown):

clear_solution_info(self: ortools.sat.python.cp_model_helper.CpSolverResponse) -> None

def clear_solve_log(unknown):
class CpSolverStatus(pybind11_builtins.pybind11_object):

Members:

UNKNOWN

MODEL_INVALID

FEASIBLE

INFEASIBLE

OPTIMAL

CpSolverStatus()

__init__(self: ortools.sat.python.cp_model_helper.CpSolverStatus, value: int) -> None

name

name(self: object) -> str

UNKNOWN = <CpSolverStatus.UNKNOWN: 0>
MODEL_INVALID = <CpSolverStatus.MODEL_INVALID: 1>
FEASIBLE = <CpSolverStatus.FEASIBLE: 2>
INFEASIBLE = <CpSolverStatus.INFEASIBLE: 3>
OPTIMAL = <CpSolverStatus.OPTIMAL: 4>
class Domain(pybind11_builtins.pybind11_object):

We call domain any subset of Int64 = [kint64min, kint64max].

This class can be used to represent such set efficiently as a sorted and non-adjacent list of intervals. This is efficient as long as the size of such list stays reasonable.

In the comments below, the domain of *this will always be written 'D'. Note that all the functions are safe with respect to integer overflow.

Domain()

__init__(self: ortools.util.python.sorted_interval_list.Domain, arg0: int, arg1: int) -> None

By default, Domain will be empty.

def all_values(unknown):

all_values() -> ortools.util.python.sorted_interval_list.Domain

Returns the full domain Int64.

def greater_or_equal(unknown):

greater_or_equal(value: int) -> ortools.util.python.sorted_interval_list.Domain

Returns the domain [value., int_max].

def from_values(unknown):

from_values(values: list[int]) -> ortools.util.python.sorted_interval_list.Domain

Creates a domain from the union of an unsorted list of integer values. Input values may be repeated, with no consequence on the output

def from_intervals(unknown):

from_intervals(intervals: list[list[int]]) -> ortools.util.python.sorted_interval_list.Domain

This method is available in Python, Java and .NET. It allows building a Domain object from a list of intervals (long[][] in Java and .NET, [[0, 2], [5, 5], [8, 10]] in python).

def from_flat_intervals(unknown):

from_flat_intervals(flat_intervals: list[int]) -> ortools.util.python.sorted_interval_list.Domain

This method is available in Python, Java and .NET. It allows building a Domain object from a flattened list of intervals (long[] in Java and .NET, [0, 2, 5, 5, 8, 10] in python).

def lower_or_equal(unknown):

lower_or_equal(value: int) -> ortools.util.python.sorted_interval_list.Domain

Returns the domain [int_min, value].

def addition_with(unknown):

addition_with(self: ortools.util.python.sorted_interval_list.Domain, domain: ortools.util.python.sorted_interval_list.Domain) -> ortools.util.python.sorted_interval_list.Domain

Returns {x ∈ Int64, ∃ a ∈ D, ∃ b ∈ domain, x = a + b}.

def complement(unknown):
def contains(unknown):

contains(self: ortools.util.python.sorted_interval_list.Domain, value: int) -> bool

Returns true iff value is in Domain.

def flattened_intervals(unknown):

flattened_intervals(self: ortools.util.python.sorted_interval_list.Domain) -> list[int]

This method returns the flattened list of interval bounds of the domain.

Thus the domain {0, 1, 2, 5, 8, 9, 10} will return [0, 2, 5, 5, 8, 10] (as a C++ std::vector, as a java or C# long[], as a python list of integers).

def intersection_with(unknown):
def is_empty(unknown):

is_empty(self: ortools.util.python.sorted_interval_list.Domain) -> bool

Returns true if this is the empty set.

def is_included_in(unknown):

is_included_in(self: ortools.util.python.sorted_interval_list.Domain, domain: ortools.util.python.sorted_interval_list.Domain) -> bool

Returns true iff D is included in the given domain.

def size(unknown):

size(self: ortools.util.python.sorted_interval_list.Domain) -> int

Returns the number of elements in the domain. It is capped at kint64max

def max(unknown):

max(self: ortools.util.python.sorted_interval_list.Domain) -> int

Returns the max value of the domain. The domain must not be empty.

def min(unknown):

min(self: ortools.util.python.sorted_interval_list.Domain) -> int

Returns the min value of the domain. The domain must not be empty.

def negation(unknown):

negation(self: ortools.util.python.sorted_interval_list.Domain) -> ortools.util.python.sorted_interval_list.Domain

Returns {x ∈ Int64, ∃ e ∈ D, x = -e}.

Note in particular that if the negation of Int64 is not Int64 but Int64 \ {kint64min} !!

def overlaps_with(unknown):

overlaps_with(self: ortools.util.python.sorted_interval_list.Domain, domain: ortools.util.python.sorted_interval_list.Domain) -> bool

  • Returns true iff D overlaps with the given domain, that is, the intersection of the two domains is not empty.
def union_with(unknown):
def AllValues(unknown):

AllValues() -> ortools.util.python.sorted_interval_list.Domain

Returns the full domain Int64.

def FromValues(unknown):

FromValues(values: list[int]) -> ortools.util.python.sorted_interval_list.Domain

Creates a domain from the union of an unsorted list of integer values. Input values may be repeated, with no consequence on the output

def FromIntervals(unknown):

FromIntervals(intervals: list[list[int]]) -> ortools.util.python.sorted_interval_list.Domain

This method is available in Python, Java and .NET. It allows building a Domain object from a list of intervals (long[][] in Java and .NET, [[0, 2], [5, 5], [8, 10]] in python).

def FromFlatIntervals(unknown):

FromFlatIntervals(flat_intervals: list[int]) -> ortools.util.python.sorted_interval_list.Domain

This method is available in Python, Java and .NET. It allows building a Domain object from a flattened list of intervals (long[] in Java and .NET, [0, 2, 5, 5, 8, 10] in python).

def FlattenedIntervals(unknown):

FlattenedIntervals(self: ortools.util.python.sorted_interval_list.Domain) -> list[int]

This method returns the flattened list of interval bounds of the domain.

Thus the domain {0, 1, 2, 5, 8, 9, 10} will return [0, 2, 5, 5, 8, 10] (as a C++ std::vector, as a java or C# long[], as a python list of integers).

A flattened and optimized floating point linear expression.

It can be used to cache complex expressions as parsing them is only done once.

vars

(arg0: ortools.sat.python.cp_model_helper.FlatFloatExpr) -> list[operations_research::sat::python::IntVar]

coeffs

A flattened and optimized integer linear expression.

It can be used to cache complex expressions as parsing them is only done once.

vars

(arg0: ortools.sat.python.cp_model_helper.FlatIntExpr) -> list[operations_research::sat::python::IntVar]

class IntervalVar(pybind11_builtins.pybind11_object):

Represents an Interval variable.

An interval variable is both a constraint and a variable. It is defined by three integer variables: start, size, and end.

It is a constraint because, internally, it enforces that start + size == end.

It is also a variable as it can appear in specific scheduling constraints: NoOverlap, NoOverlap2D, Cumulative.

Optionally, an enforcement literal can be added to this constraint, in which case these scheduling constraints will ignore interval variables with enforcement literals assigned to false. Conversely, these constraints will also set these enforcement literals to false if they cannot fit these intervals into the schedule.

Raises:
  • ValueError: if start, size, end are not defined, or have the wrong type.
IntervalVar()

__init__(self: ortools.sat.python.cp_model_helper.IntervalVar, arg0: operations_research::sat::CpModelProto, arg1: int) -> None

model_proto

(arg0: ortools.sat.python.cp_model_helper.IntervalVar) -> operations_research::sat::CpModelProto

proto

(arg0: ortools.sat.python.cp_model_helper.IntervalVar) -> operations_research::sat::ConstraintProto

def start_expr(unknown):

start_expr(self: ortools.sat.python.cp_model_helper.IntervalVar) -> object

Returns the start expression of the interval variable.

def size_expr(unknown):

size_expr(self: ortools.sat.python.cp_model_helper.IntervalVar) -> object

Returns the size expression of the interval variable.

def end_expr(unknown):

end_expr(self: ortools.sat.python.cp_model_helper.IntervalVar) -> object

Returns the end expression of the interval variable.

def presence_literals(unknown):

presence_literals(self: ortools.sat.python.cp_model_helper.IntervalVar) -> list[ortools.sat.python.cp_model_helper.Literal]

Returns the list of enforcement literals of the interval variable.

def Proto(unknown):

Proto(self: ortools.sat.python.cp_model_helper.IntervalVar) -> operations_research::sat::ConstraintProto

def Index(unknown):
def Name(unknown):
def StartExpr(unknown):
def SizeExpr(unknown):
def EndExpr(unknown):

A class to hold an integer or Boolean variable

IntVar()

__init__(args, *kwargs) Overloaded function.

  1. __init__(self: ortools.sat.python.cp_model_helper.IntVar, arg0: operations_research::sat::CpModelProto, arg1: int) -> None

  2. __init__(self: ortools.sat.python.cp_model_helper.IntVar, arg0: operations_research::sat::CpModelProto) -> None

proto

(arg0: ortools.sat.python.cp_model_helper.IntVar) -> operations_research::sat::IntegerVariableProto

model_proto

(arg0: ortools.sat.python.cp_model_helper.IntVar) -> operations_research::sat::CpModelProto

is_boolean
def with_name(unknown):

with_name(self: ortools.sat.python.cp_model_helper.IntVar, name: str) -> ortools.sat.python.cp_model_helper.IntVar

Sets the name of the variable and returns the variable.

def with_domain(unknown):

with_domain(self: ortools.sat.python.cp_model_helper.IntVar, domain: ortools.util.python.sorted_interval_list.Domain) -> ortools.sat.python.cp_model_helper.IntVar

Sets the domain of the variable and returns the variable.

def negated(unknown):

negated(self: ortools.sat.python.cp_model_helper.IntVar) -> ortools.sat.python.cp_model_helper.Literal

Returns the negation of the current variable.

def Name(unknown):
def Proto(unknown):

Proto(self: ortools.sat.python.cp_model_helper.IntVar) -> operations_research::sat::IntegerVariableProto

def Index(unknown):
class LinearExpr(pybind11_builtins.pybind11_object):

A class to hold an integer or floating point linear expression.

A linear expression is built from (integer or floating point) constants and variables. For example, x + 2 * (y - z + 1).

Linear expressions are used in CP-SAT models in constraints and in the objective.

Note that constraints only accept linear expressions with integral coefficients and constants. On the other hand, The objective can be a linear expression with floating point coefficients and constants.

You can define linear constraints as in:

model.add(x + 2 * y <= 5)
model.add(sum(array_of_vars) == 5)
  • In CP-SAT, the objective is a linear expression:
model.minimize(x + 2 * y + z)
  • For large arrays, using the LinearExpr class is faster that using the python sum() function. You can create constraints and the objective from lists of linear expressions or coefficients as follows:
model.minimize(cp_model.LinearExpr.sum(expressions))
model.add(cp_model.LinearExpr.weighted_sum(expressions, coefficients) >= 0)
LinearExpr(*args, **kwargs)
def sum(unknown):

sum(*args) -> ortools.sat.python.cp_model_helper.LinearExpr

Returns the sum(expressions).

def weighted_sum(unknown):

weighted_sum(expressions: Sequence, coefficients: Sequence) -> ortools.sat.python.cp_model_helper.LinearExpr

Returns the sum of (expressions[i] * coefficients[i])

def term(unknown):

term(args, *kwargs) Overloaded function.

  1. term(expr: ortools.sat.python.cp_model_helper.LinearExpr, coeff: int) -> ortools.sat.python.cp_model_helper.LinearExpr

Returns expr * coeff.

  1. term(expr: ortools.sat.python.cp_model_helper.LinearExpr, coeff: float) -> ortools.sat.python.cp_model_helper.LinearExpr

Returns expr * coeff.

def affine(unknown):

affine(args, *kwargs) Overloaded function.

  1. affine(expr: ortools.sat.python.cp_model_helper.LinearExpr, coeff: int, offset: int) -> ortools.sat.python.cp_model_helper.LinearExpr

Returns expr * coeff + offset.

  1. affine(expr: ortools.sat.python.cp_model_helper.LinearExpr, coeff: float, offset: float) -> ortools.sat.python.cp_model_helper.LinearExpr

Returns expr * coeff + offset.

def constant(unknown):

constant(args, *kwargs) Overloaded function.

  1. constant(value: int) -> ortools.sat.python.cp_model_helper.LinearExpr

Returns a new LinearExpr that is the given constant.

  1. constant(value: float) -> ortools.sat.python.cp_model_helper.LinearExpr

Returns a new LinearExpr that is the given constant.

def Sum(unknown):
def WeightedSum(unknown):

WeightedSum(expressions: Sequence, coefficients: Sequence) -> ortools.sat.python.cp_model_helper.LinearExpr

def Term(unknown):

Term(args, *kwargs) Overloaded function.

  1. Term(expr: ortools.sat.python.cp_model_helper.LinearExpr, coeff: int) -> ortools.sat.python.cp_model_helper.LinearExpr

Returns expr * coeff.

  1. Term(expr: ortools.sat.python.cp_model_helper.LinearExpr, coeff: float) -> ortools.sat.python.cp_model_helper.LinearExpr

Returns expr * coeff.

def is_integer(unknown):
class NotBooleanVariable(ortools.sat.python.cp_model_helper.Literal):

A class to hold a negated variable index.

NotBooleanVariable(*args, **kwargs)
def negated(unknown):

negated(self: ortools.sat.python.cp_model_helper.NotBooleanVariable) -> ortools.sat.python.cp_model_helper.Literal

Returns the negation of the current literal, that is the original Boolean variable.

def Not(unknown):

Not(self: ortools.sat.python.cp_model_helper.NotBooleanVariable) -> ortools.sat.python.cp_model_helper.Literal

Returns the negation of the current literal, that is the original Boolean variable.

class SatParameters(pybind11_builtins.pybind11_object):
SatParameters()
def merge_text_format(unknown):

merge_text_format(self: ortools.sat.python.cp_model_helper.SatParameters, arg0: str) -> bool

def parse_text_format(unknown):

parse_text_format(self: ortools.sat.python.cp_model_helper.SatParameters, arg0: str) -> bool

def clear_name(unknown):
preferred_variable_order

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> operations_research::sat::SatParameters_VariableOrder

def clear_preferred_variable_order(unknown):

clear_preferred_variable_order(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

initial_polarity

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> operations_research::sat::SatParameters_Polarity

def clear_initial_polarity(unknown):

clear_initial_polarity(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_phase_saving
def clear_use_phase_saving(unknown):

clear_use_phase_saving(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

polarity_rephase_increment
def clear_polarity_rephase_increment(unknown):

clear_polarity_rephase_increment(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

polarity_exploit_ls_hints
def clear_polarity_exploit_ls_hints(unknown):

clear_polarity_exploit_ls_hints(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

random_polarity_ratio
def clear_random_polarity_ratio(unknown):

clear_random_polarity_ratio(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

random_branches_ratio
def clear_random_branches_ratio(unknown):

clear_random_branches_ratio(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_erwa_heuristic
def clear_use_erwa_heuristic(unknown):

clear_use_erwa_heuristic(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

initial_variables_activity
def clear_initial_variables_activity(unknown):

clear_initial_variables_activity(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

also_bump_variables_in_conflict_reasons
def clear_also_bump_variables_in_conflict_reasons(unknown):

clear_also_bump_variables_in_conflict_reasons(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

minimization_algorithm

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> operations_research::sat::SatParameters_ConflictMinimizationAlgorithm

def clear_minimization_algorithm(unknown):

clear_minimization_algorithm(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

binary_minimization_algorithm

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> operations_research::sat::SatParameters_BinaryMinizationAlgorithm

def clear_binary_minimization_algorithm(unknown):

clear_binary_minimization_algorithm(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

subsumption_during_conflict_analysis
def clear_subsumption_during_conflict_analysis(unknown):

clear_subsumption_during_conflict_analysis(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

extra_subsumption_during_conflict_analysis
def clear_extra_subsumption_during_conflict_analysis(unknown):

clear_extra_subsumption_during_conflict_analysis(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

decision_subsumption_during_conflict_analysis
def clear_decision_subsumption_during_conflict_analysis(unknown):

clear_decision_subsumption_during_conflict_analysis(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

eagerly_subsume_last_n_conflicts
def clear_eagerly_subsume_last_n_conflicts(unknown):

clear_eagerly_subsume_last_n_conflicts(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

subsume_during_vivification
def clear_subsume_during_vivification(unknown):

clear_subsume_during_vivification(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_chronological_backtracking
def clear_use_chronological_backtracking(unknown):

clear_use_chronological_backtracking(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_backjump_levels
def clear_max_backjump_levels(unknown):

clear_max_backjump_levels(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

chronological_backtrack_min_conflicts
def clear_chronological_backtrack_min_conflicts(unknown):

clear_chronological_backtrack_min_conflicts(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

clause_cleanup_period
def clear_clause_cleanup_period(unknown):

clear_clause_cleanup_period(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

clause_cleanup_period_increment
def clear_clause_cleanup_period_increment(unknown):

clear_clause_cleanup_period_increment(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

clause_cleanup_target
def clear_clause_cleanup_target(unknown):

clear_clause_cleanup_target(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

clause_cleanup_ratio
def clear_clause_cleanup_ratio(unknown):

clear_clause_cleanup_ratio(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

clause_cleanup_lbd_bound
def clear_clause_cleanup_lbd_bound(unknown):

clear_clause_cleanup_lbd_bound(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

clause_cleanup_lbd_tier1
def clear_clause_cleanup_lbd_tier1(unknown):

clear_clause_cleanup_lbd_tier1(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

clause_cleanup_lbd_tier2
def clear_clause_cleanup_lbd_tier2(unknown):

clear_clause_cleanup_lbd_tier2(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

clause_cleanup_ordering

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> operations_research::sat::SatParameters_ClauseOrdering

def clear_clause_cleanup_ordering(unknown):

clear_clause_cleanup_ordering(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

pb_cleanup_increment
def clear_pb_cleanup_increment(unknown):

clear_pb_cleanup_increment(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

pb_cleanup_ratio
def clear_pb_cleanup_ratio(unknown):

clear_pb_cleanup_ratio(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

variable_activity_decay
def clear_variable_activity_decay(unknown):

clear_variable_activity_decay(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_variable_activity_value
def clear_max_variable_activity_value(unknown):

clear_max_variable_activity_value(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

glucose_max_decay
def clear_glucose_max_decay(unknown):

clear_glucose_max_decay(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

glucose_decay_increment
def clear_glucose_decay_increment(unknown):

clear_glucose_decay_increment(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

glucose_decay_increment_period
def clear_glucose_decay_increment_period(unknown):

clear_glucose_decay_increment_period(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

clause_activity_decay
def clear_clause_activity_decay(unknown):

clear_clause_activity_decay(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_clause_activity_value
def clear_max_clause_activity_value(unknown):

clear_max_clause_activity_value(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

restart_algorithms

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> google::protobuf::RepeatedField

default_restart_algorithms
def clear_default_restart_algorithms(unknown):

clear_default_restart_algorithms(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

restart_period
def clear_restart_period(unknown):

clear_restart_period(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

restart_running_window_size
def clear_restart_running_window_size(unknown):

clear_restart_running_window_size(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

restart_dl_average_ratio
def clear_restart_dl_average_ratio(unknown):

clear_restart_dl_average_ratio(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

restart_lbd_average_ratio
def clear_restart_lbd_average_ratio(unknown):

clear_restart_lbd_average_ratio(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_blocking_restart
def clear_use_blocking_restart(unknown):

clear_use_blocking_restart(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

blocking_restart_window_size
def clear_blocking_restart_window_size(unknown):

clear_blocking_restart_window_size(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

blocking_restart_multiplier
def clear_blocking_restart_multiplier(unknown):

clear_blocking_restart_multiplier(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

num_conflicts_before_strategy_changes
def clear_num_conflicts_before_strategy_changes(unknown):

clear_num_conflicts_before_strategy_changes(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

strategy_change_increase_ratio
def clear_strategy_change_increase_ratio(unknown):

clear_strategy_change_increase_ratio(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_time_in_seconds
def clear_max_time_in_seconds(unknown):

clear_max_time_in_seconds(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_deterministic_time
def clear_max_deterministic_time(unknown):

clear_max_deterministic_time(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_num_deterministic_batches
def clear_max_num_deterministic_batches(unknown):

clear_max_num_deterministic_batches(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_number_of_conflicts
def clear_max_number_of_conflicts(unknown):

clear_max_number_of_conflicts(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_memory_in_mb
def clear_max_memory_in_mb(unknown):

clear_max_memory_in_mb(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

absolute_gap_limit
def clear_absolute_gap_limit(unknown):

clear_absolute_gap_limit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

relative_gap_limit
def clear_relative_gap_limit(unknown):

clear_relative_gap_limit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

def clear_random_seed(unknown):

clear_random_seed(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

permute_variable_randomly
def clear_permute_variable_randomly(unknown):

clear_permute_variable_randomly(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

permute_presolve_constraint_order
def clear_permute_presolve_constraint_order(unknown):

clear_permute_presolve_constraint_order(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_absl_random
def clear_use_absl_random(unknown):

clear_use_absl_random(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

log_search_progress
def clear_log_search_progress(unknown):

clear_log_search_progress(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

log_subsolver_statistics
def clear_log_subsolver_statistics(unknown):

clear_log_subsolver_statistics(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

def clear_log_prefix(unknown):

clear_log_prefix(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

log_to_stdout
def clear_log_to_stdout(unknown):

clear_log_to_stdout(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

log_to_response
def clear_log_to_response(unknown):

clear_log_to_response(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_pb_resolution
def clear_use_pb_resolution(unknown):

clear_use_pb_resolution(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

minimize_reduction_during_pb_resolution
def clear_minimize_reduction_during_pb_resolution(unknown):

clear_minimize_reduction_during_pb_resolution(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

count_assumption_levels_in_lbd
def clear_count_assumption_levels_in_lbd(unknown):

clear_count_assumption_levels_in_lbd(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

presolve_bve_threshold
def clear_presolve_bve_threshold(unknown):

clear_presolve_bve_threshold(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

filter_sat_postsolve_clauses
def clear_filter_sat_postsolve_clauses(unknown):

clear_filter_sat_postsolve_clauses(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

presolve_bve_clause_weight
def clear_presolve_bve_clause_weight(unknown):

clear_presolve_bve_clause_weight(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

probing_deterministic_time_limit
def clear_probing_deterministic_time_limit(unknown):

clear_probing_deterministic_time_limit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

presolve_probing_deterministic_time_limit
def clear_presolve_probing_deterministic_time_limit(unknown):

clear_presolve_probing_deterministic_time_limit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

presolve_blocked_clause
def clear_presolve_blocked_clause(unknown):

clear_presolve_blocked_clause(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

presolve_use_bva
def clear_presolve_use_bva(unknown):

clear_presolve_use_bva(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

presolve_bva_threshold
def clear_presolve_bva_threshold(unknown):

clear_presolve_bva_threshold(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_presolve_iterations
def clear_max_presolve_iterations(unknown):

clear_max_presolve_iterations(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

cp_model_presolve
def clear_cp_model_presolve(unknown):

clear_cp_model_presolve(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

cp_model_probing_level
def clear_cp_model_probing_level(unknown):

clear_cp_model_probing_level(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

cp_model_use_sat_presolve
def clear_cp_model_use_sat_presolve(unknown):

clear_cp_model_use_sat_presolve(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

load_at_most_ones_in_sat_presolve
def clear_load_at_most_ones_in_sat_presolve(unknown):

clear_load_at_most_ones_in_sat_presolve(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

remove_fixed_variables_early
def clear_remove_fixed_variables_early(unknown):

clear_remove_fixed_variables_early(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

detect_table_with_cost
def clear_detect_table_with_cost(unknown):

clear_detect_table_with_cost(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

table_compression_level
def clear_table_compression_level(unknown):

clear_table_compression_level(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

expand_alldiff_constraints
def clear_expand_alldiff_constraints(unknown):

clear_expand_alldiff_constraints(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_alldiff_domain_size
def clear_max_alldiff_domain_size(unknown):

clear_max_alldiff_domain_size(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

expand_reservoir_constraints
def clear_expand_reservoir_constraints(unknown):

clear_expand_reservoir_constraints(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_domain_size_for_linear2_expansion
def clear_max_domain_size_for_linear2_expansion(unknown):

clear_max_domain_size_for_linear2_expansion(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

expand_reservoir_using_circuit
def clear_expand_reservoir_using_circuit(unknown):

clear_expand_reservoir_using_circuit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

encode_cumulative_as_reservoir
def clear_encode_cumulative_as_reservoir(unknown):

clear_encode_cumulative_as_reservoir(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_lin_max_size_for_expansion
def clear_max_lin_max_size_for_expansion(unknown):

clear_max_lin_max_size_for_expansion(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

disable_constraint_expansion
def clear_disable_constraint_expansion(unknown):

clear_disable_constraint_expansion(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

encode_complex_linear_constraint_with_integer
def clear_encode_complex_linear_constraint_with_integer(unknown):

clear_encode_complex_linear_constraint_with_integer(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

merge_no_overlap_work_limit
def clear_merge_no_overlap_work_limit(unknown):

clear_merge_no_overlap_work_limit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

merge_at_most_one_work_limit
def clear_merge_at_most_one_work_limit(unknown):

clear_merge_at_most_one_work_limit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

presolve_substitution_level
def clear_presolve_substitution_level(unknown):

clear_presolve_substitution_level(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

presolve_extract_integer_enforcement
def clear_presolve_extract_integer_enforcement(unknown):

clear_presolve_extract_integer_enforcement(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

presolve_inclusion_work_limit
def clear_presolve_inclusion_work_limit(unknown):

clear_presolve_inclusion_work_limit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

def clear_ignore_names(unknown):

clear_ignore_names(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

infer_all_diffs
def clear_infer_all_diffs(unknown):

clear_infer_all_diffs(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

find_big_linear_overlap
def clear_find_big_linear_overlap(unknown):

clear_find_big_linear_overlap(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

find_clauses_that_are_exactly_one
def clear_find_clauses_that_are_exactly_one(unknown):

clear_find_clauses_that_are_exactly_one(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_sat_inprocessing
def clear_use_sat_inprocessing(unknown):

clear_use_sat_inprocessing(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

inprocessing_dtime_ratio
def clear_inprocessing_dtime_ratio(unknown):

clear_inprocessing_dtime_ratio(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

inprocessing_probing_dtime
def clear_inprocessing_probing_dtime(unknown):

clear_inprocessing_probing_dtime(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

inprocessing_minimization_dtime
def clear_inprocessing_minimization_dtime(unknown):

clear_inprocessing_minimization_dtime(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

inprocessing_minimization_use_conflict_analysis
def clear_inprocessing_minimization_use_conflict_analysis(unknown):

clear_inprocessing_minimization_use_conflict_analysis(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

inprocessing_minimization_use_all_orderings
def clear_inprocessing_minimization_use_all_orderings(unknown):

clear_inprocessing_minimization_use_all_orderings(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

inprocessing_use_congruence_closure
def clear_inprocessing_use_congruence_closure(unknown):

clear_inprocessing_use_congruence_closure(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

inprocessing_use_sat_sweeping
def clear_inprocessing_use_sat_sweeping(unknown):

clear_inprocessing_use_sat_sweeping(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

def clear_num_workers(unknown):

clear_num_workers(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

num_search_workers
def clear_num_search_workers(unknown):

clear_num_search_workers(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

num_full_subsolvers
def clear_num_full_subsolvers(unknown):

clear_num_full_subsolvers(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

subsolvers

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> google::protobuf::RepeatedPtrField, std::allocator > >

extra_subsolvers

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> google::protobuf::RepeatedPtrField, std::allocator > >

ignore_subsolvers

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> google::protobuf::RepeatedPtrField, std::allocator > >

filter_subsolvers

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> google::protobuf::RepeatedPtrField, std::allocator > >

subsolver_params

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> google::protobuf::RepeatedPtrField

interleave_batch_size
def clear_interleave_batch_size(unknown):

clear_interleave_batch_size(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

share_objective_bounds
def clear_share_objective_bounds(unknown):

clear_share_objective_bounds(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

share_level_zero_bounds
def clear_share_level_zero_bounds(unknown):

clear_share_level_zero_bounds(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

share_linear2_bounds
def clear_share_linear2_bounds(unknown):

clear_share_linear2_bounds(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

share_binary_clauses
def clear_share_binary_clauses(unknown):

clear_share_binary_clauses(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

share_glue_clauses
def clear_share_glue_clauses(unknown):

clear_share_glue_clauses(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

minimize_shared_clauses
def clear_minimize_shared_clauses(unknown):

clear_minimize_shared_clauses(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

share_glue_clauses_dtime
def clear_share_glue_clauses_dtime(unknown):

clear_share_glue_clauses_dtime(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

check_lrat_proof
def clear_check_lrat_proof(unknown):

clear_check_lrat_proof(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

check_merged_lrat_proof
def clear_check_merged_lrat_proof(unknown):

clear_check_merged_lrat_proof(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

output_lrat_proof
def clear_output_lrat_proof(unknown):

clear_output_lrat_proof(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

check_drat_proof
def clear_check_drat_proof(unknown):

clear_check_drat_proof(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

output_drat_proof
def clear_output_drat_proof(unknown):

clear_output_drat_proof(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_drat_time_in_seconds
def clear_max_drat_time_in_seconds(unknown):

clear_max_drat_time_in_seconds(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

debug_postsolve_with_full_solver
def clear_debug_postsolve_with_full_solver(unknown):

clear_debug_postsolve_with_full_solver(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

debug_max_num_presolve_operations
def clear_debug_max_num_presolve_operations(unknown):

clear_debug_max_num_presolve_operations(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

debug_crash_on_bad_hint
def clear_debug_crash_on_bad_hint(unknown):

clear_debug_crash_on_bad_hint(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

debug_crash_if_presolve_breaks_hint
def clear_debug_crash_if_presolve_breaks_hint(unknown):

clear_debug_crash_if_presolve_breaks_hint(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

debug_crash_if_lrat_check_fails
def clear_debug_crash_if_lrat_check_fails(unknown):

clear_debug_crash_if_lrat_check_fails(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_optimization_hints
def clear_use_optimization_hints(unknown):

clear_use_optimization_hints(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

core_minimization_level
def clear_core_minimization_level(unknown):

clear_core_minimization_level(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

find_multiple_cores
def clear_find_multiple_cores(unknown):

clear_find_multiple_cores(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

cover_optimization
def clear_cover_optimization(unknown):

clear_cover_optimization(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_sat_assumption_order

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> operations_research::sat::SatParameters_MaxSatAssumptionOrder

def clear_max_sat_assumption_order(unknown):

clear_max_sat_assumption_order(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_sat_reverse_assumption_order
def clear_max_sat_reverse_assumption_order(unknown):

clear_max_sat_reverse_assumption_order(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_sat_stratification

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> operations_research::sat::SatParameters_MaxSatStratificationAlgorithm

def clear_max_sat_stratification(unknown):

clear_max_sat_stratification(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

propagation_loop_detection_factor
def clear_propagation_loop_detection_factor(unknown):

clear_propagation_loop_detection_factor(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_precedences_in_disjunctive_constraint
def clear_use_precedences_in_disjunctive_constraint(unknown):

clear_use_precedences_in_disjunctive_constraint(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

transitive_precedences_work_limit
def clear_transitive_precedences_work_limit(unknown):

clear_transitive_precedences_work_limit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_size_to_create_precedence_literals_in_disjunctive
def clear_max_size_to_create_precedence_literals_in_disjunctive(unknown):

clear_max_size_to_create_precedence_literals_in_disjunctive(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_strong_propagation_in_disjunctive
def clear_use_strong_propagation_in_disjunctive(unknown):

clear_use_strong_propagation_in_disjunctive(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_dynamic_precedence_in_disjunctive
def clear_use_dynamic_precedence_in_disjunctive(unknown):

clear_use_dynamic_precedence_in_disjunctive(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_dynamic_precedence_in_cumulative
def clear_use_dynamic_precedence_in_cumulative(unknown):

clear_use_dynamic_precedence_in_cumulative(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_overload_checker_in_cumulative
def clear_use_overload_checker_in_cumulative(unknown):

clear_use_overload_checker_in_cumulative(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_conservative_scale_overload_checker
def clear_use_conservative_scale_overload_checker(unknown):

clear_use_conservative_scale_overload_checker(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_timetable_edge_finding_in_cumulative
def clear_use_timetable_edge_finding_in_cumulative(unknown):

clear_use_timetable_edge_finding_in_cumulative(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_num_intervals_for_timetable_edge_finding
def clear_max_num_intervals_for_timetable_edge_finding(unknown):

clear_max_num_intervals_for_timetable_edge_finding(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_hard_precedences_in_cumulative
def clear_use_hard_precedences_in_cumulative(unknown):

clear_use_hard_precedences_in_cumulative(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

exploit_all_precedences
def clear_exploit_all_precedences(unknown):

clear_exploit_all_precedences(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_disjunctive_constraint_in_cumulative
def clear_use_disjunctive_constraint_in_cumulative(unknown):

clear_use_disjunctive_constraint_in_cumulative(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

no_overlap_2d_boolean_relations_limit
def clear_no_overlap_2d_boolean_relations_limit(unknown):

clear_no_overlap_2d_boolean_relations_limit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_timetabling_in_no_overlap_2d
def clear_use_timetabling_in_no_overlap_2d(unknown):

clear_use_timetabling_in_no_overlap_2d(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_energetic_reasoning_in_no_overlap_2d
def clear_use_energetic_reasoning_in_no_overlap_2d(unknown):

clear_use_energetic_reasoning_in_no_overlap_2d(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_area_energetic_reasoning_in_no_overlap_2d
def clear_use_area_energetic_reasoning_in_no_overlap_2d(unknown):

clear_use_area_energetic_reasoning_in_no_overlap_2d(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_try_edge_reasoning_in_no_overlap_2d
def clear_use_try_edge_reasoning_in_no_overlap_2d(unknown):

clear_use_try_edge_reasoning_in_no_overlap_2d(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_pairs_pairwise_reasoning_in_no_overlap_2d
def clear_max_pairs_pairwise_reasoning_in_no_overlap_2d(unknown):

clear_max_pairs_pairwise_reasoning_in_no_overlap_2d(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

maximum_regions_to_split_in_disconnected_no_overlap_2d
def clear_maximum_regions_to_split_in_disconnected_no_overlap_2d(unknown):

clear_maximum_regions_to_split_in_disconnected_no_overlap_2d(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_linear3_for_no_overlap_2d_precedences
def clear_use_linear3_for_no_overlap_2d_precedences(unknown):

clear_use_linear3_for_no_overlap_2d_precedences(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_dual_scheduling_heuristics
def clear_use_dual_scheduling_heuristics(unknown):

clear_use_dual_scheduling_heuristics(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_all_different_for_circuit
def clear_use_all_different_for_circuit(unknown):

clear_use_all_different_for_circuit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

routing_cut_subset_size_for_binary_relation_bound
def clear_routing_cut_subset_size_for_binary_relation_bound(unknown):

clear_routing_cut_subset_size_for_binary_relation_bound(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

routing_cut_subset_size_for_tight_binary_relation_bound
def clear_routing_cut_subset_size_for_tight_binary_relation_bound(unknown):

clear_routing_cut_subset_size_for_tight_binary_relation_bound(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

routing_cut_subset_size_for_exact_binary_relation_bound
def clear_routing_cut_subset_size_for_exact_binary_relation_bound(unknown):

clear_routing_cut_subset_size_for_exact_binary_relation_bound(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

routing_cut_subset_size_for_shortest_paths_bound
def clear_routing_cut_subset_size_for_shortest_paths_bound(unknown):

clear_routing_cut_subset_size_for_shortest_paths_bound(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

routing_cut_dp_effort
def clear_routing_cut_dp_effort(unknown):

clear_routing_cut_dp_effort(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

routing_cut_max_infeasible_path_length
def clear_routing_cut_max_infeasible_path_length(unknown):

clear_routing_cut_max_infeasible_path_length(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

search_branching

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> operations_research::sat::SatParameters_SearchBranching

def clear_search_branching(unknown):

clear_search_branching(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

hint_conflict_limit
def clear_hint_conflict_limit(unknown):

clear_hint_conflict_limit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

def clear_repair_hint(unknown):

clear_repair_hint(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

fix_variables_to_their_hinted_value
def clear_fix_variables_to_their_hinted_value(unknown):

clear_fix_variables_to_their_hinted_value(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_extended_probing
def clear_use_extended_probing(unknown):

clear_use_extended_probing(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

probing_num_combinations_limit
def clear_probing_num_combinations_limit(unknown):

clear_probing_num_combinations_limit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

shaving_search_deterministic_time
def clear_shaving_search_deterministic_time(unknown):

clear_shaving_search_deterministic_time(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

shaving_search_threshold
def clear_shaving_search_threshold(unknown):

clear_shaving_search_threshold(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

variables_shaving_level
def clear_variables_shaving_level(unknown):

clear_variables_shaving_level(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

pseudo_cost_reliability_threshold
def clear_pseudo_cost_reliability_threshold(unknown):

clear_pseudo_cost_reliability_threshold(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

optimize_with_core
def clear_optimize_with_core(unknown):

clear_optimize_with_core(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

binary_search_num_conflicts
def clear_binary_search_num_conflicts(unknown):

clear_binary_search_num_conflicts(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

optimize_with_max_hs
def clear_optimize_with_max_hs(unknown):

clear_optimize_with_max_hs(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_feasibility_jump
def clear_use_feasibility_jump(unknown):

clear_use_feasibility_jump(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

def clear_use_ls_only(unknown):

clear_use_ls_only(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

feasibility_jump_decay
def clear_feasibility_jump_decay(unknown):

clear_feasibility_jump_decay(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

feasibility_jump_linearization_level
def clear_feasibility_jump_linearization_level(unknown):

clear_feasibility_jump_linearization_level(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

feasibility_jump_restart_factor
def clear_feasibility_jump_restart_factor(unknown):

clear_feasibility_jump_restart_factor(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

feasibility_jump_batch_dtime
def clear_feasibility_jump_batch_dtime(unknown):

clear_feasibility_jump_batch_dtime(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

feasibility_jump_var_randomization_probability
def clear_feasibility_jump_var_randomization_probability(unknown):

clear_feasibility_jump_var_randomization_probability(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

feasibility_jump_var_perburbation_range_ratio
def clear_feasibility_jump_var_perburbation_range_ratio(unknown):

clear_feasibility_jump_var_perburbation_range_ratio(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

feasibility_jump_enable_restarts
def clear_feasibility_jump_enable_restarts(unknown):

clear_feasibility_jump_enable_restarts(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

feasibility_jump_max_expanded_constraint_size
def clear_feasibility_jump_max_expanded_constraint_size(unknown):

clear_feasibility_jump_max_expanded_constraint_size(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

num_violation_ls
def clear_num_violation_ls(unknown):

clear_num_violation_ls(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

violation_ls_perturbation_period
def clear_violation_ls_perturbation_period(unknown):

clear_violation_ls_perturbation_period(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

violation_ls_compound_move_probability
def clear_violation_ls_compound_move_probability(unknown):

clear_violation_ls_compound_move_probability(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

shared_tree_num_workers
def clear_shared_tree_num_workers(unknown):

clear_shared_tree_num_workers(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

shared_tree_worker_min_restarts_per_subtree
def clear_shared_tree_worker_min_restarts_per_subtree(unknown):

clear_shared_tree_worker_min_restarts_per_subtree(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

shared_tree_worker_enable_trail_sharing
def clear_shared_tree_worker_enable_trail_sharing(unknown):

clear_shared_tree_worker_enable_trail_sharing(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

shared_tree_worker_enable_phase_sharing
def clear_shared_tree_worker_enable_phase_sharing(unknown):

clear_shared_tree_worker_enable_phase_sharing(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

shared_tree_open_leaves_per_worker
def clear_shared_tree_open_leaves_per_worker(unknown):

clear_shared_tree_open_leaves_per_worker(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

shared_tree_max_nodes_per_worker
def clear_shared_tree_max_nodes_per_worker(unknown):

clear_shared_tree_max_nodes_per_worker(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

shared_tree_split_strategy

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> operations_research::sat::SatParameters_SharedTreeSplitStrategy

def clear_shared_tree_split_strategy(unknown):

clear_shared_tree_split_strategy(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

shared_tree_balance_tolerance
def clear_shared_tree_balance_tolerance(unknown):

clear_shared_tree_balance_tolerance(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

shared_tree_split_min_dtime
def clear_shared_tree_split_min_dtime(unknown):

clear_shared_tree_split_min_dtime(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

enumerate_all_solutions
def clear_enumerate_all_solutions(unknown):

clear_enumerate_all_solutions(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

keep_all_feasible_solutions_in_presolve
def clear_keep_all_feasible_solutions_in_presolve(unknown):

clear_keep_all_feasible_solutions_in_presolve(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

fill_tightened_domains_in_response
def clear_fill_tightened_domains_in_response(unknown):

clear_fill_tightened_domains_in_response(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

fill_additional_solutions_in_response
def clear_fill_additional_solutions_in_response(unknown):

clear_fill_additional_solutions_in_response(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

instantiate_all_variables
def clear_instantiate_all_variables(unknown):

clear_instantiate_all_variables(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

auto_detect_greater_than_at_least_one_of
def clear_auto_detect_greater_than_at_least_one_of(unknown):

clear_auto_detect_greater_than_at_least_one_of(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

stop_after_first_solution
def clear_stop_after_first_solution(unknown):

clear_stop_after_first_solution(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

stop_after_presolve
def clear_stop_after_presolve(unknown):

clear_stop_after_presolve(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

stop_after_root_propagation
def clear_stop_after_root_propagation(unknown):

clear_stop_after_root_propagation(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

lns_initial_difficulty
def clear_lns_initial_difficulty(unknown):

clear_lns_initial_difficulty(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

lns_initial_deterministic_limit
def clear_lns_initial_deterministic_limit(unknown):

clear_lns_initial_deterministic_limit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

def clear_use_lns(unknown):
def clear_use_lns_only(unknown):

clear_use_lns_only(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

solution_pool_size
def clear_solution_pool_size(unknown):

clear_solution_pool_size(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

solution_pool_diversity_limit
def clear_solution_pool_diversity_limit(unknown):

clear_solution_pool_diversity_limit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

alternative_pool_size
def clear_alternative_pool_size(unknown):

clear_alternative_pool_size(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

def clear_use_rins_lns(unknown):

clear_use_rins_lns(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_feasibility_pump
def clear_use_feasibility_pump(unknown):

clear_use_feasibility_pump(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_lb_relax_lns
def clear_use_lb_relax_lns(unknown):

clear_use_lb_relax_lns(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

lb_relax_num_workers_threshold
def clear_lb_relax_num_workers_threshold(unknown):

clear_lb_relax_num_workers_threshold(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

fp_rounding

(arg0: ortools.sat.python.cp_model_helper.SatParameters) -> operations_research::sat::SatParameters_FPRoundingMethod

def clear_fp_rounding(unknown):

clear_fp_rounding(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

diversify_lns_params
def clear_diversify_lns_params(unknown):

clear_diversify_lns_params(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

search_random_variable_pool_size
def clear_search_random_variable_pool_size(unknown):

clear_search_random_variable_pool_size(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

push_all_tasks_toward_start
def clear_push_all_tasks_toward_start(unknown):

clear_push_all_tasks_toward_start(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_optional_variables
def clear_use_optional_variables(unknown):

clear_use_optional_variables(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_exact_lp_reason
def clear_use_exact_lp_reason(unknown):

clear_use_exact_lp_reason(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_combined_no_overlap
def clear_use_combined_no_overlap(unknown):

clear_use_combined_no_overlap(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

at_most_one_max_expansion_size
def clear_at_most_one_max_expansion_size(unknown):

clear_at_most_one_max_expansion_size(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

catch_sigint_signal
def clear_catch_sigint_signal(unknown):

clear_catch_sigint_signal(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_implied_bounds
def clear_use_implied_bounds(unknown):

clear_use_implied_bounds(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

polish_lp_solution
def clear_polish_lp_solution(unknown):

clear_polish_lp_solution(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

lp_primal_tolerance
def clear_lp_primal_tolerance(unknown):

clear_lp_primal_tolerance(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

lp_dual_tolerance
def clear_lp_dual_tolerance(unknown):

clear_lp_dual_tolerance(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

convert_intervals
def clear_convert_intervals(unknown):

clear_convert_intervals(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

symmetry_level
def clear_symmetry_level(unknown):

clear_symmetry_level(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_symmetry_in_lp
def clear_use_symmetry_in_lp(unknown):

clear_use_symmetry_in_lp(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

keep_symmetry_in_presolve
def clear_keep_symmetry_in_presolve(unknown):

clear_keep_symmetry_in_presolve(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

symmetry_detection_deterministic_time_limit
def clear_symmetry_detection_deterministic_time_limit(unknown):

clear_symmetry_detection_deterministic_time_limit(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

new_linear_propagation
def clear_new_linear_propagation(unknown):

clear_new_linear_propagation(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

linear_split_size
def clear_linear_split_size(unknown):

clear_linear_split_size(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

linearization_level
def clear_linearization_level(unknown):

clear_linearization_level(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

boolean_encoding_level
def clear_boolean_encoding_level(unknown):

clear_boolean_encoding_level(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_domain_size_when_encoding_eq_neq_constraints
def clear_max_domain_size_when_encoding_eq_neq_constraints(unknown):

clear_max_domain_size_when_encoding_eq_neq_constraints(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

def clear_max_num_cuts(unknown):

clear_max_num_cuts(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

def clear_cut_level(unknown):

clear_cut_level(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

only_add_cuts_at_level_zero
def clear_only_add_cuts_at_level_zero(unknown):

clear_only_add_cuts_at_level_zero(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

add_objective_cut
def clear_add_objective_cut(unknown):

clear_add_objective_cut(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

def clear_add_cg_cuts(unknown):

clear_add_cg_cuts(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

def clear_add_mir_cuts(unknown):

clear_add_mir_cuts(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

add_zero_half_cuts
def clear_add_zero_half_cuts(unknown):

clear_add_zero_half_cuts(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

add_clique_cuts
def clear_add_clique_cuts(unknown):

clear_add_clique_cuts(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

def clear_add_rlt_cuts(unknown):

clear_add_rlt_cuts(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_all_diff_cut_size
def clear_max_all_diff_cut_size(unknown):

clear_max_all_diff_cut_size(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

add_lin_max_cuts
def clear_add_lin_max_cuts(unknown):

clear_add_lin_max_cuts(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_integer_rounding_scaling
def clear_max_integer_rounding_scaling(unknown):

clear_max_integer_rounding_scaling(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

add_lp_constraints_lazily
def clear_add_lp_constraints_lazily(unknown):

clear_add_lp_constraints_lazily(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

root_lp_iterations
def clear_root_lp_iterations(unknown):

clear_root_lp_iterations(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

min_orthogonality_for_lp_constraints
def clear_min_orthogonality_for_lp_constraints(unknown):

clear_min_orthogonality_for_lp_constraints(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_cut_rounds_at_level_zero
def clear_max_cut_rounds_at_level_zero(unknown):

clear_max_cut_rounds_at_level_zero(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

max_consecutive_inactive_count
def clear_max_consecutive_inactive_count(unknown):

clear_max_consecutive_inactive_count(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

cut_max_active_count_value
def clear_cut_max_active_count_value(unknown):

clear_cut_max_active_count_value(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

cut_active_count_decay
def clear_cut_active_count_decay(unknown):

clear_cut_active_count_decay(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

cut_cleanup_target
def clear_cut_cleanup_target(unknown):

clear_cut_cleanup_target(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

new_constraints_batch_size
def clear_new_constraints_batch_size(unknown):

clear_new_constraints_batch_size(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

exploit_integer_lp_solution
def clear_exploit_integer_lp_solution(unknown):

clear_exploit_integer_lp_solution(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

exploit_all_lp_solution
def clear_exploit_all_lp_solution(unknown):

clear_exploit_all_lp_solution(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

exploit_best_solution
def clear_exploit_best_solution(unknown):

clear_exploit_best_solution(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

exploit_relaxation_solution
def clear_exploit_relaxation_solution(unknown):

clear_exploit_relaxation_solution(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

exploit_objective
def clear_exploit_objective(unknown):

clear_exploit_objective(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

detect_linearized_product
def clear_detect_linearized_product(unknown):

clear_detect_linearized_product(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

use_new_integer_conflict_resolution
def clear_use_new_integer_conflict_resolution(unknown):

clear_use_new_integer_conflict_resolution(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

create_1uip_boolean_during_icr
def clear_create_1uip_boolean_during_icr(unknown):

clear_create_1uip_boolean_during_icr(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

mip_max_bound
def clear_mip_max_bound(unknown):

clear_mip_max_bound(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

mip_var_scaling
def clear_mip_var_scaling(unknown):

clear_mip_var_scaling(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

mip_scale_large_domain
def clear_mip_scale_large_domain(unknown):

clear_mip_scale_large_domain(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

mip_automatically_scale_variables
def clear_mip_automatically_scale_variables(unknown):

clear_mip_automatically_scale_variables(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

only_solve_ip
def clear_only_solve_ip(unknown):

clear_only_solve_ip(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

mip_wanted_precision
def clear_mip_wanted_precision(unknown):

clear_mip_wanted_precision(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

mip_max_activity_exponent
def clear_mip_max_activity_exponent(unknown):

clear_mip_max_activity_exponent(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

mip_check_precision
def clear_mip_check_precision(unknown):

clear_mip_check_precision(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

mip_compute_true_objective_bound
def clear_mip_compute_true_objective_bound(unknown):

clear_mip_compute_true_objective_bound(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

mip_max_valid_magnitude
def clear_mip_max_valid_magnitude(unknown):

clear_mip_max_valid_magnitude(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

mip_treat_high_magnitude_bounds_as_infinity
def clear_mip_treat_high_magnitude_bounds_as_infinity(unknown):

clear_mip_treat_high_magnitude_bounds_as_infinity(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

mip_drop_tolerance
def clear_mip_drop_tolerance(unknown):

clear_mip_drop_tolerance(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

mip_presolve_level
def clear_mip_presolve_level(unknown):

clear_mip_presolve_level(self: ortools.sat.python.cp_model_helper.SatParameters) -> None

STRATIFICATION_NONE = <MaxSatStratificationAlgorithm.STRATIFICATION_NONE: 0>
STRATIFICATION_DESCENT = <MaxSatStratificationAlgorithm.STRATIFICATION_DESCENT: 1>
STRATIFICATION_ASCENT = <MaxSatStratificationAlgorithm.STRATIFICATION_ASCENT: 2>
POLARITY_TRUE = <Polarity.POLARITY_TRUE: 0>
POLARITY_FALSE = <Polarity.POLARITY_FALSE: 1>
POLARITY_RANDOM = <Polarity.POLARITY_RANDOM: 2>
IN_ORDER = <VariableOrder.IN_ORDER: 0>
IN_REVERSE_ORDER = <VariableOrder.IN_REVERSE_ORDER: 1>
IN_RANDOM_ORDER = <VariableOrder.IN_RANDOM_ORDER: 2>
NONE = <ConflictMinimizationAlgorithm.NONE: 0>
SIMPLE = <ConflictMinimizationAlgorithm.SIMPLE: 1>
RECURSIVE = <ConflictMinimizationAlgorithm.RECURSIVE: 2>
CLAUSE_ACTIVITY = <ClauseOrdering.CLAUSE_ACTIVITY: 0>
CLAUSE_LBD = <ClauseOrdering.CLAUSE_LBD: 1>
DEFAULT_ASSUMPTION_ORDER = <MaxSatAssumptionOrder.DEFAULT_ASSUMPTION_ORDER: 0>
ORDER_ASSUMPTION_BY_DEPTH = <MaxSatAssumptionOrder.ORDER_ASSUMPTION_BY_DEPTH: 1>
ORDER_ASSUMPTION_BY_WEIGHT = <MaxSatAssumptionOrder.ORDER_ASSUMPTION_BY_WEIGHT: 2>
NO_BINARY_MINIMIZATION = <BinaryMinizationAlgorithm.NO_BINARY_MINIMIZATION: 0>
BINARY_MINIMIZATION_FROM_UIP = <BinaryMinizationAlgorithm.BINARY_MINIMIZATION_FROM_UIP: 1>
BINARY_MINIMIZATION_FROM_UIP_AND_DECISIONS = <BinaryMinizationAlgorithm.BINARY_MINIMIZATION_FROM_UIP_AND_DECISIONS: 5>
SPLIT_STRATEGY_AUTO = <SharedTreeSplitStrategy.SPLIT_STRATEGY_AUTO: 0>
SPLIT_STRATEGY_DISCREPANCY = <SharedTreeSplitStrategy.SPLIT_STRATEGY_DISCREPANCY: 1>
SPLIT_STRATEGY_OBJECTIVE_LB = <SharedTreeSplitStrategy.SPLIT_STRATEGY_OBJECTIVE_LB: 2>
SPLIT_STRATEGY_BALANCED_TREE = <SharedTreeSplitStrategy.SPLIT_STRATEGY_BALANCED_TREE: 3>
SPLIT_STRATEGY_FIRST_PROPOSAL = <SharedTreeSplitStrategy.SPLIT_STRATEGY_FIRST_PROPOSAL: 4>
NEAREST_INTEGER = <FPRoundingMethod.NEAREST_INTEGER: 0>
LOCK_BASED = <FPRoundingMethod.LOCK_BASED: 1>
ACTIVE_LOCK_BASED = <FPRoundingMethod.ACTIVE_LOCK_BASED: 3>
PROPAGATION_ASSISTED = <FPRoundingMethod.PROPAGATION_ASSISTED: 2>
class SatParameters.SearchBranching(pybind11_builtins.pybind11_object):

Members:

AUTOMATIC_SEARCH

FIXED_SEARCH

PORTFOLIO_SEARCH

LP_SEARCH

PSEUDO_COST_SEARCH

PORTFOLIO_WITH_QUICK_RESTART_SEARCH

HINT_SEARCH

PARTIAL_FIXED_SEARCH

RANDOMIZED_SEARCH

SatParameters.SearchBranching()
name

name(self: object) -> str

class SatParameters.MaxSatStratificationAlgorithm(pybind11_builtins.pybind11_object):

Members:

STRATIFICATION_NONE

STRATIFICATION_DESCENT

STRATIFICATION_ASCENT

SatParameters.MaxSatStratificationAlgorithm()
name

name(self: object) -> str

STRATIFICATION_NONE = <MaxSatStratificationAlgorithm.STRATIFICATION_NONE: 0>
STRATIFICATION_DESCENT = <MaxSatStratificationAlgorithm.STRATIFICATION_DESCENT: 1>
STRATIFICATION_ASCENT = <MaxSatStratificationAlgorithm.STRATIFICATION_ASCENT: 2>
class SatParameters.Polarity(pybind11_builtins.pybind11_object):

Members:

POLARITY_TRUE

POLARITY_FALSE

POLARITY_RANDOM

SatParameters.Polarity()
name

name(self: object) -> str

POLARITY_TRUE = <Polarity.POLARITY_TRUE: 0>
POLARITY_FALSE = <Polarity.POLARITY_FALSE: 1>
POLARITY_RANDOM = <Polarity.POLARITY_RANDOM: 2>
class SatParameters.VariableOrder(pybind11_builtins.pybind11_object):

Members:

IN_ORDER

IN_REVERSE_ORDER

IN_RANDOM_ORDER

SatParameters.VariableOrder()
name

name(self: object) -> str

IN_ORDER = <VariableOrder.IN_ORDER: 0>
IN_REVERSE_ORDER = <VariableOrder.IN_REVERSE_ORDER: 1>
IN_RANDOM_ORDER = <VariableOrder.IN_RANDOM_ORDER: 2>
class SatParameters.ConflictMinimizationAlgorithm(pybind11_builtins.pybind11_object):

Members:

NONE

SIMPLE

RECURSIVE

SatParameters.ConflictMinimizationAlgorithm()
name

name(self: object) -> str

NONE = <ConflictMinimizationAlgorithm.NONE: 0>
SIMPLE = <ConflictMinimizationAlgorithm.SIMPLE: 1>
RECURSIVE = <ConflictMinimizationAlgorithm.RECURSIVE: 2>
class SatParameters.ClauseOrdering(pybind11_builtins.pybind11_object):

Members:

CLAUSE_ACTIVITY

CLAUSE_LBD

SatParameters.ClauseOrdering()
name

name(self: object) -> str

CLAUSE_ACTIVITY = <ClauseOrdering.CLAUSE_ACTIVITY: 0>
CLAUSE_LBD = <ClauseOrdering.CLAUSE_LBD: 1>
class SatParameters.MaxSatAssumptionOrder(pybind11_builtins.pybind11_object):

Members:

DEFAULT_ASSUMPTION_ORDER

ORDER_ASSUMPTION_BY_DEPTH

ORDER_ASSUMPTION_BY_WEIGHT

SatParameters.MaxSatAssumptionOrder()
name

name(self: object) -> str

DEFAULT_ASSUMPTION_ORDER = <MaxSatAssumptionOrder.DEFAULT_ASSUMPTION_ORDER: 0>
ORDER_ASSUMPTION_BY_DEPTH = <MaxSatAssumptionOrder.ORDER_ASSUMPTION_BY_DEPTH: 1>
ORDER_ASSUMPTION_BY_WEIGHT = <MaxSatAssumptionOrder.ORDER_ASSUMPTION_BY_WEIGHT: 2>
class SatParameters.BinaryMinizationAlgorithm(pybind11_builtins.pybind11_object):

Members:

NO_BINARY_MINIMIZATION

BINARY_MINIMIZATION_FROM_UIP

BINARY_MINIMIZATION_FROM_UIP_AND_DECISIONS

SatParameters.BinaryMinizationAlgorithm()
name

name(self: object) -> str

NO_BINARY_MINIMIZATION = <BinaryMinizationAlgorithm.NO_BINARY_MINIMIZATION: 0>
BINARY_MINIMIZATION_FROM_UIP = <BinaryMinizationAlgorithm.BINARY_MINIMIZATION_FROM_UIP: 1>
BINARY_MINIMIZATION_FROM_UIP_AND_DECISIONS = <BinaryMinizationAlgorithm.BINARY_MINIMIZATION_FROM_UIP_AND_DECISIONS: 5>
class SatParameters.SharedTreeSplitStrategy(pybind11_builtins.pybind11_object):

Members:

SPLIT_STRATEGY_AUTO

SPLIT_STRATEGY_DISCREPANCY

SPLIT_STRATEGY_OBJECTIVE_LB

SPLIT_STRATEGY_BALANCED_TREE

SPLIT_STRATEGY_FIRST_PROPOSAL

SatParameters.SharedTreeSplitStrategy()
name

name(self: object) -> str

SPLIT_STRATEGY_AUTO = <SharedTreeSplitStrategy.SPLIT_STRATEGY_AUTO: 0>
SPLIT_STRATEGY_DISCREPANCY = <SharedTreeSplitStrategy.SPLIT_STRATEGY_DISCREPANCY: 1>
SPLIT_STRATEGY_OBJECTIVE_LB = <SharedTreeSplitStrategy.SPLIT_STRATEGY_OBJECTIVE_LB: 2>
SPLIT_STRATEGY_BALANCED_TREE = <SharedTreeSplitStrategy.SPLIT_STRATEGY_BALANCED_TREE: 3>
SPLIT_STRATEGY_FIRST_PROPOSAL = <SharedTreeSplitStrategy.SPLIT_STRATEGY_FIRST_PROPOSAL: 4>
class SatParameters.FPRoundingMethod(pybind11_builtins.pybind11_object):

Members:

NEAREST_INTEGER

LOCK_BASED

ACTIVE_LOCK_BASED

PROPAGATION_ASSISTED

SatParameters.FPRoundingMethod()
name

name(self: object) -> str

NEAREST_INTEGER = <FPRoundingMethod.NEAREST_INTEGER: 0>
LOCK_BASED = <FPRoundingMethod.LOCK_BASED: 1>
ACTIVE_LOCK_BASED = <FPRoundingMethod.ACTIVE_LOCK_BASED: 3>
PROPAGATION_ASSISTED = <FPRoundingMethod.PROPAGATION_ASSISTED: 2>
INT_MIN = -9223372036854775808
INT_MAX = 9223372036854775807
INT32_MIN = -2147483648
INT32_MAX = 2147483647
UNKNOWN = <CpSolverStatus.UNKNOWN: 0>
MODEL_INVALID = <CpSolverStatus.MODEL_INVALID: 1>
FEASIBLE = <CpSolverStatus.FEASIBLE: 2>
INFEASIBLE = <CpSolverStatus.INFEASIBLE: 3>
OPTIMAL = <CpSolverStatus.OPTIMAL: 4>
CHOOSE_FIRST = <VariableSelectionStrategy.CHOOSE_FIRST: 0>
CHOOSE_LOWEST_MIN = <VariableSelectionStrategy.CHOOSE_LOWEST_MIN: 1>
CHOOSE_HIGHEST_MAX = <VariableSelectionStrategy.CHOOSE_HIGHEST_MAX: 2>
CHOOSE_MIN_DOMAIN_SIZE = <VariableSelectionStrategy.CHOOSE_MIN_DOMAIN_SIZE: 3>
CHOOSE_MAX_DOMAIN_SIZE = <VariableSelectionStrategy.CHOOSE_MAX_DOMAIN_SIZE: 4>
SELECT_MIN_VALUE = <DomainReductionStrategy.SELECT_MIN_VALUE: 0>
SELECT_MAX_VALUE = <DomainReductionStrategy.SELECT_MAX_VALUE: 1>
SELECT_LOWER_HALF = <DomainReductionStrategy.SELECT_LOWER_HALF: 2>
SELECT_UPPER_HALF = <DomainReductionStrategy.SELECT_UPPER_HALF: 3>
SELECT_MEDIAN_VALUE = <DomainReductionStrategy.SELECT_MEDIAN_VALUE: 4>
SELECT_RANDOM_HALF = <DomainReductionStrategy.SELECT_RANDOM_HALF: 5>
IntegralT = int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64
IntegralTypes = (<class 'int'>, <class 'numpy.int8'>, <class 'numpy.uint8'>, <class 'numpy.int32'>, <class 'numpy.uint32'>, <class 'numpy.int64'>, <class 'numpy.uint64'>)
NumberT = int | float | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | numpy.float64
NumberTypes = (<class 'int'>, <class 'float'>, <class 'numpy.int8'>, <class 'numpy.uint8'>, <class 'numpy.int32'>, <class 'numpy.uint32'>, <class 'numpy.int64'>, <class 'numpy.uint64'>, <class 'numpy.float64'>)
LiteralT = ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool
VariableT = ForwardRef('IntVar') | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64
LinearExprT = LinearExpr | ForwardRef('IntVar') | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64
ObjLinearExprT = LinearExpr | int | float | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | numpy.float64
ArcT = tuple[int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool]
enable_warnings = False
def deprecated(message: str) -> Callable[[Callable], Callable]:
192def deprecated(message: str) -> Callable[[Callable], Callable]:
193    """Decorator that warns about a deprecated function."""
194
195    def deprecated_decorator(func) -> Callable:
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)
205
206        return deprecated_func
207
208    return deprecated_decorator

Decorator that warns about a deprecated function.

def deprecated_method(func, old_name: str) -> Callable:
211def deprecated_method(func, old_name: str) -> Callable:
212    """Wrapper that warns about a deprecated method."""
213
214    def deprecated_func(*args, **kwargs) -> Any:
215        if enable_warnings:
216            warnings.warn(
217                f"{old_name} is a deprecated function. Use {func.__name__} instead.",
218                category=DeprecationWarning,
219                stacklevel=2,
220            )
221            warnings.simplefilter("default", DeprecationWarning)
222        return func(*args, **kwargs)
223
224    return deprecated_func

Wrapper that warns about a deprecated method.

def snake_case_to_camel_case(name: str) -> str:
230def snake_case_to_camel_case(name: str) -> str:
231    """Converts a snake_case name to CamelCase."""
232    words = name.split("_")
233    return (
234        "".join(word.capitalize() for word in words)
235        .replace("2d", "2D")
236        .replace("Xor", "XOr")
237    )

Converts a snake_case name to CamelCase.

def object_is_a_true_literal( literal: ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool) -> bool:
240def object_is_a_true_literal(literal: LiteralT) -> bool:
241    """Checks if literal is either True, or a Boolean literals fixed to True."""
242    if isinstance(literal, IntVar):
243        proto = literal.proto
244        return len(proto.domain) == 2 and proto.domain[0] == 1 and proto.domain[1] == 1
245    if isinstance(literal, cmh.NotBooleanVariable):
246        proto = literal.negated().proto
247        return len(proto.domain) == 2 and proto.domain[0] == 0 and proto.domain[1] == 0
248    if isinstance(literal, (bool, np.bool_)):
249        return bool(literal)
250    if isinstance(literal, IntegralTypes):
251        literal_as_int = int(literal)
252        return literal_as_int == 1 or literal_as_int == ~False
253    return False

Checks if literal is either True, or a Boolean literals fixed to True.

def object_is_a_false_literal( literal: ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool) -> bool:
256def object_is_a_false_literal(literal: LiteralT) -> bool:
257    """Checks if literal is either False, or a Boolean literals fixed to False."""
258    if isinstance(literal, IntVar):
259        proto = literal.proto
260        return len(proto.domain) == 2 and proto.domain[0] == 0 and proto.domain[1] == 0
261    if isinstance(literal, cmh.NotBooleanVariable):
262        proto = literal.negated().proto
263        return len(proto.domain) == 2 and proto.domain[0] == 1 and proto.domain[1] == 1
264    if isinstance(literal, (bool, np.bool_)):
265        return not bool(literal)
266    if isinstance(literal, IntegralTypes):
267        literal_as_int = int(literal)
268        return literal_as_int == 0 or literal_as_int == ~True
269    return False

Checks if literal is either False, or a Boolean literals fixed to False.

 307class CpModel(cmh.CpBaseModel):
 308    """Methods for building a CP model.
 309
 310    Methods beginning with:
 311
 312    * ```new_``` create integer, boolean, or interval variables.
 313    * ```add_``` create new constraints and add them to the model.
 314    """
 315
 316    def __init__(self, model_proto: Optional[cmh.CpModelProto] = None) -> None:
 317        cmh.CpBaseModel.__init__(self, model_proto)
 318        self._add_pre_pep8_methods()
 319
 320    # Naming.
 321    @property
 322    def name(self) -> str:
 323        """Returns the name of the model."""
 324        if not self.model_proto or not self.model_proto.name:
 325            return ""
 326        return self.model_proto.name
 327
 328    @name.setter
 329    def name(self, name: str):
 330        """Sets the name of the model."""
 331        self.model_proto.name = name
 332
 333    # Integer variable.
 334    def new_int_var(self, lb: IntegralT, ub: IntegralT, name: str) -> IntVar:
 335        """Create an integer variable with domain [lb, ub].
 336
 337        The CP-SAT solver is limited to integer variables. If you have fractional
 338        values, scale them up so that they become integers; if you have strings,
 339        encode them as integers.
 340
 341        Args:
 342          lb: Lower bound for the variable.
 343          ub: Upper bound for the variable.
 344          name: The name of the variable.
 345
 346        Returns:
 347          a variable whose domain is [lb, ub].
 348        """
 349        return (
 350            IntVar(self.model_proto)
 351            .with_name(name)
 352            .with_domain(sorted_interval_list.Domain(lb, ub))
 353        )
 354
 355    def new_int_var_from_domain(
 356        self, domain: sorted_interval_list.Domain, name: str
 357    ) -> IntVar:
 358        """Create an integer variable from a domain.
 359
 360        A domain is a set of integers specified by a collection of intervals.
 361        For example, `model.new_int_var_from_domain(cp_model.
 362             Domain.from_intervals([[1, 2], [4, 6]]), 'x')`
 363
 364        Args:
 365          domain: An instance of the Domain class.
 366          name: The name of the variable.
 367
 368        Returns:
 369            a variable whose domain is the given domain.
 370        """
 371        return IntVar(self.model_proto).with_name(name).with_domain(domain)
 372
 373    def new_bool_var(self, name: str) -> IntVar:
 374        """Creates a 0-1 variable with the given name."""
 375        return (
 376            IntVar(self.model_proto)
 377            .with_name(name)
 378            .with_domain(sorted_interval_list.Domain(0, 1))
 379        )
 380
 381    def new_constant(self, value: IntegralT) -> IntVar:
 382        """Declares a constant integer."""
 383        return IntVar(self.model_proto, self.get_or_make_index_from_constant(value))
 384
 385    def new_int_var_series(
 386        self,
 387        name: str,
 388        index: pd.Index,
 389        lower_bounds: Union[IntegralT, pd.Series],
 390        upper_bounds: Union[IntegralT, pd.Series],
 391    ) -> pd.Series:
 392        """Creates a series of (scalar-valued) variables with the given name.
 393
 394        Args:
 395          name (str): Required. The name of the variable set.
 396          index (pd.Index): Required. The index to use for the variable set.
 397          lower_bounds (Union[int, pd.Series]): A lower bound for variables in the
 398            set. If a `pd.Series` is passed in, it will be based on the
 399            corresponding values of the pd.Series.
 400          upper_bounds (Union[int, pd.Series]): An upper bound for variables in the
 401            set. If a `pd.Series` is passed in, it will be based on the
 402            corresponding values of the pd.Series.
 403
 404        Returns:
 405          pd.Series: The variable set indexed by its corresponding dimensions.
 406
 407        Raises:
 408          TypeError: if the `index` is invalid (e.g. a `DataFrame`).
 409          ValueError: if the `name` is not a valid identifier or already exists.
 410          ValueError: if the `lowerbound` is greater than the `upperbound`.
 411          ValueError: if the index of `lower_bound`, or `upper_bound` does not match
 412          the input index.
 413        """
 414        if not isinstance(index, pd.Index):
 415            raise TypeError("Non-index object is used as index")
 416        if not name.isidentifier():
 417            raise ValueError(f"name={name!r} is not a valid identifier")
 418        if (
 419            isinstance(lower_bounds, IntegralTypes)
 420            and isinstance(upper_bounds, IntegralTypes)
 421            and lower_bounds > upper_bounds
 422        ):
 423            raise ValueError(
 424                f"lower_bound={lower_bounds} is greater than"
 425                f" upper_bound={upper_bounds} for variable set={name}"
 426            )
 427
 428        lower_bounds = _convert_to_series_and_validate_index(lower_bounds, index)
 429        upper_bounds = _convert_to_series_and_validate_index(upper_bounds, index)
 430        return pd.Series(
 431            index=index,
 432            data=[
 433                # pylint: disable=g-complex-comprehension
 434                IntVar(self.model_proto)
 435                .with_name(f"{name}[{i}]")
 436                .with_domain(
 437                    sorted_interval_list.Domain(lower_bounds[i], upper_bounds[i])
 438                )
 439                for i in index
 440            ],
 441        )
 442
 443    def new_bool_var_series(
 444        self,
 445        name: str,
 446        index: pd.Index,
 447    ) -> pd.Series:
 448        """Creates a series of (scalar-valued) variables with the given name.
 449
 450        Args:
 451          name (str): Required. The name of the variable set.
 452          index (pd.Index): Required. The index to use for the variable set.
 453
 454        Returns:
 455          pd.Series: The variable set indexed by its corresponding dimensions.
 456
 457        Raises:
 458          TypeError: if the `index` is invalid (e.g. a `DataFrame`).
 459          ValueError: if the `name` is not a valid identifier or already exists.
 460        """
 461        if not isinstance(index, pd.Index):
 462            raise TypeError("Non-index object is used as index")
 463        if not name.isidentifier():
 464            raise ValueError(f"name={name!r} is not a valid identifier")
 465        return pd.Series(
 466            index=index,
 467            data=[
 468                # pylint: disable=g-complex-comprehension
 469                IntVar(self.model_proto)
 470                .with_name(f"{name}[{i}]")
 471                .with_domain(sorted_interval_list.Domain(0, 1))
 472                for i in index
 473            ],
 474        )
 475
 476    # Linear constraints.
 477
 478    def add_linear_constraint(
 479        self, linear_expr: LinearExprT, lb: IntegralT, ub: IntegralT
 480    ) -> Constraint:
 481        """Adds the constraint: `lb <= linear_expr <= ub`."""
 482        return self.add_linear_expression_in_domain(
 483            linear_expr, sorted_interval_list.Domain(lb, ub)
 484        )
 485
 486    def add_linear_expression_in_domain(
 487        self,
 488        linear_expr: LinearExprT,
 489        domain: sorted_interval_list.Domain,
 490    ) -> Constraint:
 491        """Adds the constraint: `linear_expr` in `domain`."""
 492        if isinstance(linear_expr, LinearExpr):
 493            ble = BoundedLinearExpression(linear_expr, domain)
 494            if not ble.ok:
 495                raise TypeError(
 496                    "Cannot add a linear expression containing floating point"
 497                    f" coefficients or constants: {type(linear_expr).__name__!r}"
 498                )
 499            return self._add_bounded_linear_expression(ble)
 500        if isinstance(linear_expr, IntegralTypes):
 501            if not domain.contains(int(linear_expr)):
 502                return self.add_bool_or([])  # Evaluate to false.
 503            else:
 504                return self.add_bool_and([])  # Evaluate to true.
 505        raise TypeError(
 506            "not supported:"
 507            f" CpModel.add_linear_expression_in_domain({type(linear_expr).__name__!r})"
 508        )
 509
 510    def add(self, ct: Union[BoundedLinearExpression, bool, np.bool_]) -> Constraint:
 511        """Adds a `BoundedLinearExpression` to the model.
 512
 513        Args:
 514          ct: A [`BoundedLinearExpression`](#boundedlinearexpression).
 515
 516        Returns:
 517          An instance of the `Constraint` class.
 518
 519        Raises:
 520          TypeError: If the `ct` is not a `BoundedLinearExpression` or a Boolean.
 521        """
 522        if isinstance(ct, BoundedLinearExpression):
 523            return self._add_bounded_linear_expression(ct)
 524        if ct and self.is_boolean_value(ct):
 525            return self.add_bool_or([True])
 526        if not ct and self.is_boolean_value(ct):
 527            return self.add_bool_or([])  # Evaluate to false.
 528        raise TypeError(f"not supported: CpModel.add({type(ct).__name__!r})")
 529
 530    # General Integer Constraints.
 531
 532    @overload
 533    def add_all_different(self, expressions: Iterable[LinearExprT]) -> Constraint: ...
 534
 535    @overload
 536    def add_all_different(self, *expressions: LinearExprT) -> Constraint: ...
 537
 538    def add_all_different(self, *expressions):
 539        """Adds AllDifferent(expressions).
 540
 541        This constraint forces all expressions to have different values.
 542
 543        Args:
 544          *expressions: simple expressions of the form a * var + constant.
 545
 546        Returns:
 547          An instance of the `Constraint` class.
 548        """
 549        return self._add_all_different(*expressions)
 550
 551    def add_element(
 552        self,
 553        index: LinearExprT,
 554        expressions: Sequence[LinearExprT],
 555        target: LinearExprT,
 556    ) -> Constraint:
 557        """Adds the element constraint: `expressions[index] == target`.
 558
 559        Args:
 560          index: The index of the selected expression in the array. It must be an
 561            affine expression (a * var + b).
 562          expressions: A list of affine expressions.
 563          target: The expression constrained to be equal to the selected expression.
 564            It must be an affine expression (a * var + b).
 565
 566        Returns:
 567          An instance of the `Constraint` class.
 568        """
 569
 570        if not expressions:
 571            raise ValueError("add_element expects a non-empty expressions array")
 572
 573        if isinstance(index, IntegralTypes):
 574            expression: LinearExprT = list(expressions)[int(index)]
 575            return self.add(expression == target)
 576
 577        return self._add_element(index, expressions, target)
 578
 579    def add_circuit(self, arcs: Sequence[ArcT]) -> Constraint:
 580        """Adds Circuit(arcs).
 581
 582        Adds a circuit constraint from a sparse list of arcs that encode the graph.
 583
 584        A circuit is a unique Hamiltonian cycle in a subgraph of the total
 585        graph. In case a node 'i' is not in the cycle, then there must be a
 586        loop arc 'i -> i' associated with a true literal. Otherwise
 587        this constraint will fail.
 588
 589        Args:
 590          arcs: a list of arcs. An arc is a tuple (source_node, destination_node,
 591            literal). The arc is selected in the circuit if the literal is true.
 592            Both source_node and destination_node must be integers between 0 and the
 593            number of nodes - 1.
 594
 595        Returns:
 596          An instance of the `Constraint` class.
 597
 598        Raises:
 599          ValueError: If the list of arcs is empty.
 600        """
 601        if not arcs:
 602            raise ValueError("add_circuit expects a non-empty array of arcs")
 603        return self._add_circuit(arcs)
 604
 605    def add_multiple_circuit(self, arcs: Sequence[ArcT]) -> Constraint:
 606        """Adds a multiple circuit constraint, aka the 'VRP' constraint.
 607
 608        The direct graph where arc #i (from tails[i] to head[i]) is present iff
 609        literals[i] is true must satisfy this set of properties:
 610        - #incoming arcs == 1 except for node 0.
 611        - #outgoing arcs == 1 except for node 0.
 612        - for node zero, #incoming arcs == #outgoing arcs.
 613        - There are no duplicate arcs.
 614        - Self-arcs are allowed except for node 0.
 615        - There is no cycle in this graph, except through node 0.
 616
 617        Args:
 618          arcs: a list of arcs. An arc is a tuple (source_node, destination_node,
 619            literal). The arc is selected in the circuit if the literal is true.
 620            Both source_node and destination_node must be integers between 0 and the
 621            number of nodes - 1.
 622
 623        Returns:
 624          An instance of the `Constraint` class.
 625
 626        Raises:
 627          ValueError: If the list of arcs is empty.
 628        """
 629        if not arcs:
 630            raise ValueError("add_multiple_circuit expects a non-empty array of arcs")
 631        return self._add_routes(arcs)
 632
 633    def add_allowed_assignments(
 634        self,
 635        expressions: Sequence[LinearExprT],
 636        tuples_list: Iterable[Sequence[IntegralT]],
 637    ) -> Constraint:
 638        """Adds AllowedAssignments(expressions, tuples_list).
 639
 640        An AllowedAssignments constraint is a constraint on an array of affine
 641        expressions, which requires that when all expressions are assigned values,
 642        the
 643        resulting array equals one of the  tuples in `tuple_list`.
 644
 645        Args:
 646          expressions: A list of affine expressions (a * var + b).
 647          tuples_list: A list of admissible tuples. Each tuple must have the same
 648            length as the expressions, and the ith value of a tuple corresponds to
 649            the ith expression.
 650
 651        Returns:
 652          An instance of the `Constraint` class.
 653
 654        Raises:
 655          TypeError: If a tuple does not have the same size as the list of
 656              expressions.
 657          ValueError: If the array of expressions is empty.
 658        """
 659
 660        if not expressions:
 661            raise ValueError(
 662                "add_allowed_assignments expects a non-empty expressions array"
 663            )
 664
 665        return self._add_table(expressions, tuples_list, False)
 666
 667    def add_forbidden_assignments(
 668        self,
 669        expressions: Sequence[LinearExprT],
 670        tuples_list: Iterable[Sequence[IntegralT]],
 671    ) -> Constraint:
 672        """Adds add_forbidden_assignments(expressions, [tuples_list]).
 673
 674        A ForbiddenAssignments constraint is a constraint on an array of affine
 675        expressions where the list of impossible combinations is provided in the
 676        tuples list.
 677
 678        Args:
 679          expressions: A list of affine expressions (a * var + b).
 680          tuples_list: A list of forbidden tuples. Each tuple must have the same
 681            length as the expressions, and the *i*th value of a tuple corresponds to
 682            the *i*th expression.
 683
 684        Returns:
 685          An instance of the `Constraint` class.
 686
 687        Raises:
 688          TypeError: If a tuple does not have the same size as the list of
 689                     expressions.
 690          ValueError: If the array of expressions is empty.
 691        """
 692
 693        if not expressions:
 694            raise ValueError(
 695                "add_forbidden_assignments expects a non-empty expressions array"
 696            )
 697
 698        return self._add_table(expressions, tuples_list, True)
 699
 700    def add_automaton(
 701        self,
 702        transition_expressions: Sequence[LinearExprT],
 703        starting_state: IntegralT,
 704        final_states: Sequence[IntegralT],
 705        transition_triples: Sequence[tuple[IntegralT, IntegralT, IntegralT]],
 706    ) -> Constraint:
 707        """Adds an automaton constraint.
 708
 709        An automaton constraint takes a list of affine expressions (a * var + b) (of
 710        size *n*), an initial state, a set of final states, and a set of
 711        transitions. A transition is a triplet (*tail*, *transition*, *head*), where
 712        *tail* and *head* are states, and *transition* is the label of an arc from
 713        *head* to *tail*, corresponding to the value of one expression in the list
 714        of
 715        expressions.
 716
 717        This automaton will be unrolled into a flow with *n* + 1 phases. Each phase
 718        contains the possible states of the automaton. The first state contains the
 719        initial state. The last phase contains the final states.
 720
 721        Between two consecutive phases *i* and *i* + 1, the automaton creates a set
 722        of arcs. For each transition (*tail*, *transition*, *head*), it will add
 723        an arc from the state *tail* of phase *i* and the state *head* of phase
 724        *i* + 1. This arc is labeled by the value *transition* of the expression
 725        `expressions[i]`. That is, this arc can only be selected if `expressions[i]`
 726        is assigned the value *transition*.
 727
 728        A feasible solution of this constraint is an assignment of expressions such
 729        that, starting from the initial state in phase 0, there is a path labeled by
 730        the values of the expressions that ends in one of the final states in the
 731        final phase.
 732
 733        Args:
 734          transition_expressions: A non-empty list of affine expressions (a * var +
 735            b) whose values correspond to the labels of the arcs traversed by the
 736            automaton.
 737          starting_state: The initial state of the automaton.
 738          final_states: A non-empty list of admissible final states.
 739          transition_triples: A list of transitions for the automaton, in the
 740            following format (current_state, variable_value, next_state).
 741
 742        Returns:
 743          An instance of the `Constraint` class.
 744
 745        Raises:
 746          ValueError: if `transition_expressions`, `final_states`, or
 747            `transition_triples` are empty.
 748        """
 749
 750        if not transition_expressions:
 751            raise ValueError(
 752                "add_automaton expects a non-empty transition_expressions array"
 753            )
 754        if not final_states:
 755            raise ValueError("add_automaton expects some final states")
 756
 757        if not transition_triples:
 758            raise ValueError("add_automaton expects some transition triples")
 759
 760        return self._add_automaton(
 761            transition_expressions,
 762            starting_state,
 763            final_states,
 764            transition_triples,
 765        )
 766
 767    def add_inverse(
 768        self,
 769        variables: Sequence[VariableT],
 770        inverse_variables: Sequence[VariableT],
 771    ) -> Constraint:
 772        """Adds Inverse(variables, inverse_variables).
 773
 774        An inverse constraint enforces that if `variables[i]` is assigned a value
 775        `j`, then `inverse_variables[j]` is assigned a value `i`. And vice versa.
 776
 777        Args:
 778          variables: An array of integer variables.
 779          inverse_variables: An array of integer variables.
 780
 781        Returns:
 782          An instance of the `Constraint` class.
 783
 784        Raises:
 785          TypeError: if variables and inverse_variables have different lengths, or
 786              if they are empty.
 787        """
 788
 789        if not variables or not inverse_variables:
 790            raise TypeError("The Inverse constraint does not accept empty arrays")
 791        if len(variables) != len(inverse_variables):
 792            raise TypeError(
 793                "In the inverse constraint, the two array variables and"
 794                " inverse_variables must have the same length."
 795            )
 796        return self._add_inverse(variables, inverse_variables)
 797
 798    def add_reservoir_constraint(
 799        self,
 800        times: Sequence[LinearExprT],
 801        level_changes: Sequence[LinearExprT],
 802        min_level: int,
 803        max_level: int,
 804    ) -> Constraint:
 805        """Adds Reservoir(times, level_changes, min_level, max_level).
 806
 807        Maintains a reservoir level within bounds. The water level starts at 0, and
 808        at any time, it must be between min_level and max_level.
 809
 810        If the affine expression `times[i]` is assigned a value t, then the current
 811        level changes by `level_changes[i]`, which is constant, at time t.
 812
 813         Note that min level must be <= 0, and the max level must be >= 0. Please
 814         use fixed level_changes to simulate initial state.
 815
 816         Therefore, at any time:
 817             sum(level_changes[i] if times[i] <= t) in [min_level, max_level]
 818
 819        Args:
 820          times: A list of 1-var affine expressions (a * x + b) which specify the
 821            time of the filling or emptying the reservoir.
 822          level_changes: A list of integer values that specifies the amount of the
 823            emptying or filling. Currently, variable demands are not supported.
 824          min_level: At any time, the level of the reservoir must be greater or
 825            equal than the min level.
 826          max_level: At any time, the level of the reservoir must be less or equal
 827            than the max level.
 828
 829        Returns:
 830          An instance of the `Constraint` class.
 831
 832        Raises:
 833          ValueError: if max_level < min_level.
 834
 835          ValueError: if max_level < 0.
 836
 837          ValueError: if min_level > 0
 838        """
 839
 840        return self._add_reservoir(
 841            times,
 842            level_changes,
 843            [],
 844            min_level,
 845            max_level,
 846        )
 847
 848    def add_reservoir_constraint_with_active(
 849        self,
 850        times: Sequence[LinearExprT],
 851        level_changes: Sequence[LinearExprT],
 852        actives: Sequence[LiteralT],
 853        min_level: int,
 854        max_level: int,
 855    ) -> Constraint:
 856        """Adds Reservoir(times, level_changes, actives, min_level, max_level).
 857
 858        Maintains a reservoir level within bounds. The water level starts at 0, and
 859        at any time, it must be between min_level and max_level.
 860
 861        If the variable `times[i]` is assigned a value t, and `actives[i]` is
 862        `True`, then the current level changes by `level_changes[i]`, which is
 863        constant,
 864        at time t.
 865
 866         Note that min level must be <= 0, and the max level must be >= 0. Please
 867         use fixed level_changes to simulate initial state.
 868
 869         Therefore, at any time:
 870             sum(level_changes[i] * actives[i] if times[i] <= t) in [min_level,
 871             max_level]
 872
 873
 874        The array of boolean variables 'actives', if defined, indicates which
 875        actions are actually performed.
 876
 877        Args:
 878          times: A list of 1-var affine expressions (a * x + b) which specify the
 879            time of the filling or emptying the reservoir.
 880          level_changes: A list of integer values that specifies the amount of the
 881            emptying or filling. Currently, variable demands are not supported.
 882          actives: a list of boolean variables. They indicates if the
 883            emptying/refilling events actually take place.
 884          min_level: At any time, the level of the reservoir must be greater or
 885            equal than the min level.
 886          max_level: At any time, the level of the reservoir must be less or equal
 887            than the max level.
 888
 889        Returns:
 890          An instance of the `Constraint` class.
 891
 892        Raises:
 893          ValueError: if max_level < min_level.
 894
 895          ValueError: if max_level < 0.
 896
 897          ValueError: if min_level > 0
 898        """
 899
 900        if max_level < min_level:
 901            raise ValueError("Reservoir constraint must have a max_level >= min_level")
 902
 903        if max_level < 0:
 904            raise ValueError("Reservoir constraint must have a max_level >= 0")
 905
 906        if min_level > 0:
 907            raise ValueError("Reservoir constraint must have a min_level <= 0")
 908
 909        if not times:
 910            raise ValueError("Reservoir constraint must have a non-empty times array")
 911
 912        return self._add_reservoir(
 913            times,
 914            level_changes,
 915            actives,
 916            min_level,
 917            max_level,
 918        )
 919
 920    def add_map_domain(
 921        self, var: IntVar, bool_var_array: Iterable[IntVar], offset: IntegralT = 0
 922    ):
 923        """Adds `var == i + offset <=> bool_var_array[i] == true for all i`."""
 924        for i, bool_var in enumerate(bool_var_array):
 925            self.add(var == i + offset).only_enforce_if(bool_var)
 926            self.add(var != i + offset).only_enforce_if(~bool_var)
 927
 928    def add_implication(self, a: LiteralT, b: LiteralT) -> Constraint:
 929        """Adds `a => b` (`a` implies `b`)."""
 930        return self.add_bool_and(b).only_enforce_if(a)
 931
 932    @overload
 933    def add_bool_or(self, literals: Iterable[LiteralT]) -> Constraint: ...
 934
 935    @overload
 936    def add_bool_or(self, *literals: LiteralT) -> Constraint: ...
 937
 938    def add_bool_or(self, *literals):
 939        """Adds `Or(literals) == true`: sum(literals) >= 1."""
 940        return self._add_bool_argument_constraint(
 941            cmh.BoolArgumentConstraint.bool_or, *literals
 942        )
 943
 944    @overload
 945    def add_at_least_one(self, literals: Iterable[LiteralT]) -> Constraint: ...
 946
 947    @overload
 948    def add_at_least_one(self, *literals: LiteralT) -> Constraint: ...
 949
 950    def add_at_least_one(self, *literals):
 951        """Same as `add_bool_or`: `sum(literals) >= 1`."""
 952        return self._add_bool_argument_constraint(
 953            cmh.BoolArgumentConstraint.bool_or, *literals
 954        )
 955
 956    @overload
 957    def add_at_most_one(self, literals: Iterable[LiteralT]) -> Constraint: ...
 958
 959    @overload
 960    def add_at_most_one(self, *literals: LiteralT) -> Constraint: ...
 961
 962    def add_at_most_one(self, *literals) -> Constraint:
 963        """Adds `AtMostOne(literals)`: `sum(literals) <= 1`."""
 964        return self._add_bool_argument_constraint(
 965            cmh.BoolArgumentConstraint.at_most_one, *literals
 966        )
 967
 968    @overload
 969    def add_exactly_one(self, literals: Iterable[LiteralT]) -> Constraint: ...
 970
 971    @overload
 972    def add_exactly_one(self, *literals: LiteralT) -> Constraint: ...
 973
 974    def add_exactly_one(self, *literals):
 975        """Adds `ExactlyOne(literals)`: `sum(literals) == 1`."""
 976        return self._add_bool_argument_constraint(
 977            cmh.BoolArgumentConstraint.exactly_one, *literals
 978        )
 979
 980    @overload
 981    def add_bool_and(self, literals: Iterable[LiteralT]) -> Constraint: ...
 982
 983    @overload
 984    def add_bool_and(self, *literals: LiteralT) -> Constraint: ...
 985
 986    def add_bool_and(self, *literals):
 987        """Adds `And(literals) == true`."""
 988        return self._add_bool_argument_constraint(
 989            cmh.BoolArgumentConstraint.bool_and, *literals
 990        )
 991
 992    @overload
 993    def add_bool_xor(self, literals: Iterable[LiteralT]) -> Constraint: ...
 994
 995    @overload
 996    def add_bool_xor(self, *literals: LiteralT) -> Constraint: ...
 997
 998    def add_bool_xor(self, *literals):
 999        """Adds `XOr(literals) == true`.
1000
1001        In contrast to add_bool_or and add_bool_and, it does not support
1002            .only_enforce_if().
1003
1004        Args:
1005          *literals: the list of literals in the constraint.
1006
1007        Returns:
1008          An `Constraint` object.
1009        """
1010        return self._add_bool_argument_constraint(
1011            cmh.BoolArgumentConstraint.bool_xor, *literals
1012        )
1013
1014    @overload
1015    def add_min_equality(
1016        self, target: LinearExprT, expressions: Iterable[LinearExprT]
1017    ) -> Constraint: ...
1018
1019    @overload
1020    def add_min_equality(
1021        self, target: LinearExprT, *expressions: LinearExprT
1022    ) -> Constraint: ...
1023
1024    def add_min_equality(self, target, *expressions) -> Constraint:
1025        """Adds `target == Min(expressions)`."""
1026        return self._add_linear_argument_constraint(
1027            cmh.LinearArgumentConstraint.min, target, *expressions
1028        )
1029
1030    @overload
1031    def add_max_equality(
1032        self, target: LinearExprT, expressions: Iterable[LinearExprT]
1033    ) -> Constraint: ...
1034
1035    @overload
1036    def add_max_equality(
1037        self, target: LinearExprT, *expressions: LinearExprT
1038    ) -> Constraint: ...
1039
1040    def add_max_equality(self, target, *expressions) -> Constraint:
1041        """Adds `target == Max(expressions)`."""
1042        return self._add_linear_argument_constraint(
1043            cmh.LinearArgumentConstraint.max, target, *expressions
1044        )
1045
1046    def add_division_equality(
1047        self, target: LinearExprT, num: LinearExprT, denom: LinearExprT
1048    ) -> Constraint:
1049        """Adds `target == num // denom` (integer division rounded towards 0)."""
1050        return self._add_linear_argument_constraint(
1051            cmh.LinearArgumentConstraint.div, target, [num, denom]
1052        )
1053
1054    def add_abs_equality(self, target: LinearExprT, expr: LinearExprT) -> Constraint:
1055        """Adds `target == Abs(expr)`."""
1056        return self._add_linear_argument_constraint(
1057            cmh.LinearArgumentConstraint.max, target, [expr, -expr]
1058        )
1059
1060    def add_modulo_equality(
1061        self, target: LinearExprT, expr: LinearExprT, mod: LinearExprT
1062    ) -> Constraint:
1063        """Adds `target = expr % mod`.
1064
1065        It uses the C convention, that is the result is the remainder of the
1066        integral division rounded towards 0.
1067
1068            For example:
1069            * 10 % 3 = 1
1070            * -10 % 3 = -1
1071            * 10 % -3 = 1
1072            * -10 % -3 = -1
1073
1074        Args:
1075          target: the target expression.
1076          expr: the expression to compute the modulo of.
1077          mod: the modulus expression.
1078
1079        Returns:
1080          A `Constraint` object.
1081        """
1082        return self._add_linear_argument_constraint(
1083            cmh.LinearArgumentConstraint.mod, target, [expr, mod]
1084        )
1085
1086    def add_multiplication_equality(
1087        self,
1088        target: LinearExprT,
1089        *expressions: Union[Iterable[LinearExprT], LinearExprT],
1090    ) -> Constraint:
1091        """Adds `target == expressions[0] * .. * expressions[n]`."""
1092        return self._add_linear_argument_constraint(
1093            cmh.LinearArgumentConstraint.prod, target, *expressions
1094        )
1095
1096    # Scheduling support
1097
1098    def new_interval_var(
1099        self, start: LinearExprT, size: LinearExprT, end: LinearExprT, name: str
1100    ) -> IntervalVar:
1101        """Creates an interval variable from start, size, and end.
1102
1103        An interval variable is a constraint, that is itself used in other
1104        constraints like NoOverlap.
1105
1106        Internally, it ensures that `start + size == end`.
1107
1108        Args:
1109          start: The start of the interval. It must be of the form a * var + b.
1110          size: The size of the interval. It must be of the form a * var + b.
1111          end: The end of the interval. It must be of the form a * var + b.
1112          name: The name of the interval variable.
1113
1114        Returns:
1115          An `IntervalVar` object.
1116        """
1117        return self._new_interval_var(name, start, size, end, [])
1118
1119    def new_interval_var_series(
1120        self,
1121        name: str,
1122        index: pd.Index,
1123        starts: Union[LinearExprT, pd.Series],
1124        sizes: Union[LinearExprT, pd.Series],
1125        ends: Union[LinearExprT, pd.Series],
1126    ) -> pd.Series:
1127        """Creates a series of interval variables with the given name.
1128
1129        Args:
1130          name (str): Required. The name of the variable set.
1131          index (pd.Index): Required. The index to use for the variable set.
1132          starts (Union[LinearExprT, pd.Series]): The start of each interval in the
1133            set. If a `pd.Series` is passed in, it will be based on the
1134            corresponding values of the pd.Series.
1135          sizes (Union[LinearExprT, pd.Series]): The size of each interval in the
1136            set. If a `pd.Series` is passed in, it will be based on the
1137            corresponding values of the pd.Series.
1138          ends (Union[LinearExprT, pd.Series]): The ends of each interval in the
1139            set. If a `pd.Series` is passed in, it will be based on the
1140            corresponding values of the pd.Series.
1141
1142        Returns:
1143          pd.Series: The interval variable set indexed by its corresponding
1144          dimensions.
1145
1146        Raises:
1147          TypeError: if the `index` is invalid (e.g. a `DataFrame`).
1148          ValueError: if the `name` is not a valid identifier or already exists.
1149          ValueError: if the all the indexes do not match.
1150        """
1151        if not isinstance(index, pd.Index):
1152            raise TypeError("Non-index object is used as index")
1153        if not name.isidentifier():
1154            raise ValueError(f"name={name!r} is not a valid identifier")
1155
1156        starts = _convert_to_series_and_validate_index(starts, index)
1157        sizes = _convert_to_series_and_validate_index(sizes, index)
1158        ends = _convert_to_series_and_validate_index(ends, index)
1159        interval_array = []
1160        for i in index:
1161            interval_array.append(
1162                self.new_interval_var(
1163                    start=starts[i],
1164                    size=sizes[i],
1165                    end=ends[i],
1166                    name=f"{name}[{i}]",
1167                )
1168            )
1169        return pd.Series(index=index, data=interval_array)
1170
1171    def new_fixed_size_interval_var(
1172        self, start: LinearExprT, size: IntegralT, name: str
1173    ) -> IntervalVar:
1174        """Creates an interval variable from start, and a fixed size.
1175
1176        An interval variable is a constraint, that is itself used in other
1177        constraints like NoOverlap.
1178
1179        Args:
1180          start: The start of the interval. It must be of the form a * var + b.
1181          size: The size of the interval. It must be an integer value.
1182          name: The name of the interval variable.
1183
1184        Returns:
1185          An `IntervalVar` object.
1186        """
1187        return self._new_interval_var(name, start, size, start + size, [])
1188
1189    def new_fixed_size_interval_var_series(
1190        self,
1191        name: str,
1192        index: pd.Index,
1193        starts: Union[LinearExprT, pd.Series],
1194        sizes: Union[IntegralT, pd.Series],
1195    ) -> pd.Series:
1196        """Creates a series of interval variables with the given name.
1197
1198        Args:
1199          name (str): Required. The name of the variable set.
1200          index (pd.Index): Required. The index to use for the variable set.
1201          starts (Union[LinearExprT, pd.Series]): The start of each interval in the
1202            set. If a `pd.Series` is passed in, it will be based on the
1203            corresponding values of the pd.Series.
1204          sizes (Union[IntegralT, pd.Series]): The fixed size of each interval in
1205            the set. If a `pd.Series` is passed in, it will be based on the
1206            corresponding values of the pd.Series.
1207
1208        Returns:
1209          pd.Series: The interval variable set indexed by its corresponding
1210          dimensions.
1211
1212        Raises:
1213          TypeError: if the `index` is invalid (e.g. a `DataFrame`).
1214          ValueError: if the `name` is not a valid identifier or already exists.
1215          ValueError: if the all the indexes do not match.
1216        """
1217        if not isinstance(index, pd.Index):
1218            raise TypeError("Non-index object is used as index")
1219        if not name.isidentifier():
1220            raise ValueError(f"name={name!r} is not a valid identifier")
1221
1222        starts = _convert_to_series_and_validate_index(starts, index)
1223        sizes = _convert_to_series_and_validate_index(sizes, index)
1224        interval_array = []
1225        for i in index:
1226            interval_array.append(
1227                self.new_fixed_size_interval_var(
1228                    start=starts[i],
1229                    size=sizes[i],
1230                    name=f"{name}[{i}]",
1231                )
1232            )
1233        return pd.Series(index=index, data=interval_array)
1234
1235    def new_optional_interval_var(
1236        self,
1237        start: LinearExprT,
1238        size: LinearExprT,
1239        end: LinearExprT,
1240        is_present: LiteralT,
1241        name: str,
1242    ) -> IntervalVar:
1243        """Creates an optional interval var from start, size, end, and is_present.
1244
1245        An optional interval variable is a constraint, that is itself used in other
1246        constraints like NoOverlap. This constraint is protected by a presence
1247        literal that indicates if it is active or not.
1248
1249        Internally, it ensures that `is_present` implies `start + size ==
1250        end`.
1251
1252        Args:
1253          start: The start of the interval. It must be of the form a * var + b.
1254          size: The size of the interval. It must be of the form a * var + b.
1255          end: The end of the interval. It must be of the form a * var + b.
1256          is_present: A literal that indicates if the interval is active or not. A
1257            inactive interval is simply ignored by all constraints.
1258          name: The name of the interval variable.
1259
1260        Returns:
1261          An `IntervalVar` object.
1262        """
1263        return self._new_interval_var(
1264            name,
1265            start,
1266            size,
1267            end,
1268            [is_present],
1269        )
1270
1271    def new_optional_interval_var_series(
1272        self,
1273        name: str,
1274        index: pd.Index,
1275        starts: Union[LinearExprT, pd.Series],
1276        sizes: Union[LinearExprT, pd.Series],
1277        ends: Union[LinearExprT, pd.Series],
1278        are_present: Union[LiteralT, pd.Series],
1279    ) -> pd.Series:
1280        """Creates a series of interval variables with the given name.
1281
1282        Args:
1283          name (str): Required. The name of the variable set.
1284          index (pd.Index): Required. The index to use for the variable set.
1285          starts (Union[LinearExprT, pd.Series]): The start of each interval in the
1286            set. If a `pd.Series` is passed in, it will be based on the
1287            corresponding values of the pd.Series.
1288          sizes (Union[LinearExprT, pd.Series]): The size of each interval in the
1289            set. If a `pd.Series` is passed in, it will be based on the
1290            corresponding values of the pd.Series.
1291          ends (Union[LinearExprT, pd.Series]): The ends of each interval in the
1292            set. If a `pd.Series` is passed in, it will be based on the
1293            corresponding values of the pd.Series.
1294          are_present (Union[LiteralT, pd.Series]): The performed literal of each
1295            interval in the set. If a `pd.Series` is passed in, it will be based on
1296            the corresponding values of the pd.Series.
1297
1298        Returns:
1299          pd.Series: The interval variable set indexed by its corresponding
1300          dimensions.
1301
1302        Raises:
1303          TypeError: if the `index` is invalid (e.g. a `DataFrame`).
1304          ValueError: if the `name` is not a valid identifier or already exists.
1305          ValueError: if the all the indexes do not match.
1306        """
1307        if not isinstance(index, pd.Index):
1308            raise TypeError("Non-index object is used as index")
1309        if not name.isidentifier():
1310            raise ValueError(f"name={name!r} is not a valid identifier")
1311
1312        starts = _convert_to_series_and_validate_index(starts, index)
1313        sizes = _convert_to_series_and_validate_index(sizes, index)
1314        ends = _convert_to_series_and_validate_index(ends, index)
1315        are_present = _convert_to_series_and_validate_index(are_present, index)
1316
1317        interval_array = []
1318        for i in index:
1319            interval_array.append(
1320                self.new_optional_interval_var(
1321                    start=starts[i],
1322                    size=sizes[i],
1323                    end=ends[i],
1324                    is_present=are_present[i],
1325                    name=f"{name}[{i}]",
1326                )
1327            )
1328        return pd.Series(index=index, data=interval_array)
1329
1330    def new_optional_fixed_size_interval_var(
1331        self,
1332        start: LinearExprT,
1333        size: IntegralT,
1334        is_present: LiteralT,
1335        name: str,
1336    ) -> IntervalVar:
1337        """Creates an interval variable from start, and a fixed size.
1338
1339        An interval variable is a constraint, that is itself used in other
1340        constraints like NoOverlap.
1341
1342        Args:
1343          start: The start of the interval. It must be of the form a * var + b.
1344          size: The size of the interval. It must be an integer value.
1345          is_present: A literal that indicates if the interval is active or not. A
1346            inactive interval is simply ignored by all constraints.
1347          name: The name of the interval variable.
1348
1349        Returns:
1350          An `IntervalVar` object.
1351        """
1352        return self._new_interval_var(
1353            name,
1354            start,
1355            size,
1356            start + size,
1357            [is_present],
1358        )
1359
1360    def new_optional_fixed_size_interval_var_series(
1361        self,
1362        name: str,
1363        index: pd.Index,
1364        starts: Union[LinearExprT, pd.Series],
1365        sizes: Union[IntegralT, pd.Series],
1366        are_present: Union[LiteralT, pd.Series],
1367    ) -> pd.Series:
1368        """Creates a series of interval variables with the given name.
1369
1370        Args:
1371          name (str): Required. The name of the variable set.
1372          index (pd.Index): Required. The index to use for the variable set.
1373          starts (Union[LinearExprT, pd.Series]): The start of each interval in the
1374            set. If a `pd.Series` is passed in, it will be based on the
1375            corresponding values of the pd.Series.
1376          sizes (Union[IntegralT, pd.Series]): The fixed size of each interval in
1377            the set. If a `pd.Series` is passed in, it will be based on the
1378            corresponding values of the pd.Series.
1379          are_present (Union[LiteralT, pd.Series]): The performed literal of each
1380            interval in the set. If a `pd.Series` is passed in, it will be based on
1381            the corresponding values of the pd.Series.
1382
1383        Returns:
1384          pd.Series: The interval variable set indexed by its corresponding
1385          dimensions.
1386
1387        Raises:
1388          TypeError: if the `index` is invalid (e.g. a `DataFrame`).
1389          ValueError: if the `name` is not a valid identifier or already exists.
1390          ValueError: if the all the indexes do not match.
1391        """
1392        if not isinstance(index, pd.Index):
1393            raise TypeError("Non-index object is used as index")
1394        if not name.isidentifier():
1395            raise ValueError(f"name={name!r} is not a valid identifier")
1396
1397        starts = _convert_to_series_and_validate_index(starts, index)
1398        sizes = _convert_to_series_and_validate_index(sizes, index)
1399        are_present = _convert_to_series_and_validate_index(are_present, index)
1400        interval_array = []
1401        for i in index:
1402            interval_array.append(
1403                self.new_optional_fixed_size_interval_var(
1404                    start=starts[i],
1405                    size=sizes[i],
1406                    is_present=are_present[i],
1407                    name=f"{name}[{i}]",
1408                )
1409            )
1410        return pd.Series(index=index, data=interval_array)
1411
1412    def add_no_overlap(self, intervals: Iterable[IntervalVar]) -> Constraint:
1413        """Adds NoOverlap(interval_vars).
1414
1415        A NoOverlap constraint ensures that all present intervals do not overlap
1416        in time.
1417
1418        Args:
1419          intervals: The list of interval variables to constrain.
1420
1421        Returns:
1422          An instance of the `Constraint` class.
1423        """
1424        return self._add_no_overlap(intervals)
1425
1426    def add_no_overlap_2d(
1427        self,
1428        x_intervals: Iterable[IntervalVar],
1429        y_intervals: Iterable[IntervalVar],
1430    ) -> Constraint:
1431        """Adds NoOverlap2D(x_intervals, y_intervals).
1432
1433        A NoOverlap2D constraint ensures that all present rectangles do not overlap
1434        on a plane. Each rectangle is aligned with the X and Y axis, and is defined
1435        by two intervals which represent its projection onto the X and Y axis.
1436
1437        Furthermore, one box is optional if at least one of the x or y interval is
1438        optional.
1439
1440        Args:
1441          x_intervals: The X coordinates of the rectangles.
1442          y_intervals: The Y coordinates of the rectangles.
1443
1444        Returns:
1445          An instance of the `Constraint` class.
1446        """
1447        return self._add_no_overlap_2d(x_intervals, y_intervals)
1448
1449    def add_cumulative(
1450        self,
1451        intervals: Iterable[IntervalVar],
1452        demands: Iterable[LinearExprT],
1453        capacity: LinearExprT,
1454    ) -> Constraint:
1455        """Adds Cumulative(intervals, demands, capacity).
1456
1457        This constraint enforces that:
1458
1459            for all t:
1460              sum(demands[i]
1461                if (start(intervals[i]) <= t < end(intervals[i])) and
1462                (intervals[i] is present)) <= capacity
1463
1464        Args:
1465          intervals: The list of intervals.
1466          demands: The list of demands for each interval. Each demand must be >= 0.
1467            Each demand can be a 1-var affine expression (a * x + b).
1468          capacity: The maximum capacity of the cumulative constraint. It can be a
1469            1-var affine expression (a * x + b).
1470
1471        Returns:
1472          An instance of the `Constraint` class.
1473        """
1474        return self._add_cumulative(intervals, demands, capacity)
1475
1476    # Support for model cloning.
1477    def clone(self) -> "CpModel":
1478        """Reset the model, and creates a new one from a CpModelProto instance."""
1479        clone = CpModel()
1480        clone.proto.copy_from(self.proto)
1481        clone.rebuild_constant_map()
1482        return clone
1483
1484    def __copy__(self):
1485        return CpModel(self.model_proto)
1486
1487    def __deepcopy__(self, memo):
1488        return CpModel(copy.deepcopy(self.model_proto, memo))
1489
1490    def get_bool_var_from_proto_index(self, index: int) -> IntVar:
1491        """Returns an already created Boolean variable from its index."""
1492        if index < 0 or index >= len(self.model_proto.variables):
1493            raise ValueError(
1494                f"get_bool_var_from_proto_index: out of bound index {index}"
1495            )
1496        result = IntVar(self.model_proto, index)
1497        if not result.is_boolean:
1498            raise TypeError(
1499                f"get_bool_var_from_proto_index: index {index} does not reference a"
1500                " boolean variable"
1501            )
1502        return result
1503
1504    def get_int_var_from_proto_index(self, index: int) -> IntVar:
1505        """Returns an already created integer variable from its index."""
1506        if index < 0 or index >= len(self.model_proto.variables):
1507            raise ValueError(
1508                f"get_int_var_from_proto_index: out of bound index {index}"
1509            )
1510        return IntVar(self.model_proto, index)
1511
1512    def get_interval_var_from_proto_index(self, index: int) -> IntervalVar:
1513        """Returns an already created interval variable from its index."""
1514        if index < 0 or index >= len(self.model_proto.constraints):
1515            raise ValueError(
1516                f"get_interval_var_from_proto_index: out of bound index {index}"
1517            )
1518        ct = self.model_proto.constraints[index]
1519        if not ct.has_interval():
1520            raise ValueError(
1521                f"get_interval_var_from_proto_index: index {index} does not"
1522                " reference an" + " interval variable"
1523            )
1524
1525        return IntervalVar(self.model_proto, index)
1526
1527    def __str__(self) -> str:
1528        return str(self.model_proto)
1529
1530    @property
1531    def proto(self) -> cmh.CpModelProto:
1532        """Returns the underlying CpModelProto."""
1533        return self.model_proto
1534
1535    def negated(self, index: int) -> int:
1536        return -index - 1
1537
1538    def _set_objective(self, obj: ObjLinearExprT, maximize: bool):
1539        """Sets the objective of the model."""
1540        self.clear_objective()
1541        if isinstance(obj, IntegralTypes):
1542            self.model_proto.objective.offset = int(obj)
1543            self.model_proto.objective.scaling_factor = 1.0
1544        elif isinstance(obj, LinearExpr):
1545            if obj.is_integer():
1546                int_obj = cmh.FlatIntExpr(obj)
1547                for var in int_obj.vars:
1548                    self.model_proto.objective.vars.append(var.index)
1549                if maximize:
1550                    self.model_proto.objective.scaling_factor = -1.0
1551                    self.model_proto.objective.offset = -int_obj.offset
1552                    for c in int_obj.coeffs:
1553                        self.model_proto.objective.coeffs.append(-c)
1554                else:
1555                    self.model_proto.objective.scaling_factor = 1.0
1556                    self.model_proto.objective.offset = int_obj.offset
1557                    self.model_proto.objective.coeffs.extend(int_obj.coeffs)
1558            else:
1559                float_obj = cmh.FlatFloatExpr(obj)
1560                for var in float_obj.vars:
1561                    self.model_proto.floating_point_objective.vars.append(var.index)
1562                self.model_proto.floating_point_objective.coeffs.extend(
1563                    float_obj.coeffs
1564                )
1565                self.model_proto.floating_point_objective.maximize = maximize
1566                self.model_proto.floating_point_objective.offset = float_obj.offset
1567        else:
1568            raise TypeError(
1569                f"TypeError: {type(obj).__name__!r} is not a valid objective"
1570            )
1571
1572    def minimize(self, obj: ObjLinearExprT):
1573        """Sets the objective of the model to minimize(obj)."""
1574        self._set_objective(obj, maximize=False)
1575
1576    def maximize(self, obj: ObjLinearExprT):
1577        """Sets the objective of the model to maximize(obj)."""
1578        self._set_objective(obj, maximize=True)
1579
1580    def has_objective(self) -> bool:
1581        return (
1582            self.model_proto.has_objective()
1583            or self.model_proto.has_floating_point_objective()
1584        )
1585
1586    def clear_objective(self):
1587        self.model_proto.clear_objective()
1588        self.model_proto.clear_floating_point_objective()
1589
1590    def add_decision_strategy(
1591        self,
1592        variables: Iterable[IntVar],
1593        var_strategy: cmh.DecisionStrategyProto.VariableSelectionStrategy,
1594        domain_strategy: cmh.DecisionStrategyProto.DomainReductionStrategy,
1595    ) -> None:
1596        """Adds a search strategy to the model.
1597
1598        Args:
1599          variables: a list of variables this strategy will assign.
1600          var_strategy: heuristic to choose the next variable to assign.
1601          domain_strategy: heuristic to reduce the domain of the selected variable.
1602            Currently, this is advanced code: the union of all strategies added to
1603            the model must be complete, i.e. instantiates all variables. Otherwise,
1604            solve() will fail.
1605        """
1606
1607        strategy: cmh.DecisionStrategyProto = self.model_proto.search_strategy.add()
1608        for v in variables:
1609            expr = strategy.exprs.add()
1610            if v.index >= 0:
1611                expr.vars.append(v.index)
1612                expr.coeffs.append(1)
1613            else:
1614                expr.vars.append(self.negated(v.index))
1615                expr.coeffs.append(-1)
1616                expr.offset = 1
1617
1618        strategy.variable_selection_strategy = var_strategy
1619        strategy.domain_reduction_strategy = domain_strategy
1620
1621    def model_stats(self) -> str:
1622        """Returns a string containing some model statistics."""
1623        return cmh.CpSatHelper.model_stats(self.model_proto)
1624
1625    def validate(self) -> str:
1626        """Returns a string indicating that the model is invalid."""
1627        return cmh.CpSatHelper.validate_model(self.model_proto)
1628
1629    def export_to_file(self, file: str) -> bool:
1630        """Write the model as a protocol buffer to 'file'.
1631
1632        Args:
1633          file: file to write the model to. If the filename ends with 'txt', the
1634            model will be written as a text file, otherwise, the binary format will
1635            be used.
1636
1637        Returns:
1638          True if the model was correctly written.
1639        """
1640        return cmh.CpSatHelper.write_model_to_file(self.model_proto, file)
1641
1642    def remove_all_names(self) -> None:
1643        """Removes all names from the model."""
1644        self.model_proto.clear_name()
1645        for v in self.model_proto.variables:
1646            v.clear_name()
1647        for c in self.model_proto.constraints:
1648            c.clear_name()
1649
1650    @overload
1651    def add_hint(self, var: IntVar, value: int) -> None: ...
1652
1653    @overload
1654    def add_hint(self, literal: BoolVarT, value: bool) -> None: ...
1655
1656    def add_hint(self, var, value) -> None:
1657        """Adds 'var == value' as a hint to the solver."""
1658        if var.index >= 0:
1659            self.model_proto.solution_hint.vars.append(var.index)
1660            self.model_proto.solution_hint.values.append(int(value))
1661        else:
1662            self.model_proto.solution_hint.vars.append(self.negated(var.index))
1663            self.model_proto.solution_hint.values.append(int(not value))
1664
1665    def clear_hints(self):
1666        """Removes any solution hint from the model."""
1667        self.model_proto.clear_solution_hint()
1668
1669    def add_assumption(self, lit: LiteralT) -> None:
1670        """Adds the literal to the model as assumptions."""
1671        self.model_proto.assumptions.append(self.get_or_make_boolean_index(lit))
1672
1673    def add_assumptions(self, literals: Iterable[LiteralT]) -> None:
1674        """Adds the literals to the model as assumptions."""
1675        for lit in literals:
1676            self.add_assumption(lit)
1677
1678    def clear_assumptions(self) -> None:
1679        """Removes all assumptions from the model."""
1680        self.model_proto.assumptions.clear()
1681
1682    # Compatibility with pre PEP8
1683    # pylint: disable=invalid-name
1684
1685    def _add_pre_pep8_methods(self) -> None:
1686        for method_name in dir(self):
1687            if callable(getattr(self, method_name)) and (
1688                method_name.startswith("add_")
1689                or method_name.startswith("new_")
1690                or method_name.startswith("clear_")
1691            ):
1692                pre_pep8_name = snake_case_to_camel_case(method_name)
1693                setattr(
1694                    self,
1695                    pre_pep8_name,
1696                    deprecated_method(getattr(self, method_name), pre_pep8_name),
1697                )
1698
1699        for other_method_name in [
1700            "add",
1701            "clone",
1702            "get_bool_var_from_proto_index",
1703            "get_int_var_from_proto_index",
1704            "get_interval_var_from_proto_index",
1705            "minimize",
1706            "maximize",
1707            "has_objective",
1708            "model_stats",
1709            "validate",
1710            "export_to_file",
1711        ]:
1712            pre_pep8_name = snake_case_to_camel_case(other_method_name)
1713            setattr(
1714                self,
1715                pre_pep8_name,
1716                deprecated_method(getattr(self, other_method_name), pre_pep8_name),
1717            )
1718
1719    @deprecated("Use name property instead.")
1720    def Name(self) -> str:
1721        return self.name
1722
1723    @deprecated("Use name property instead.")
1724    def SetName(self, name: str) -> None:
1725        self.name = name
1726
1727    @deprecated("Use proto property instead.")
1728    def Proto(self) -> cmh.CpModelProto:
1729        return self.proto
1730
1731    # pylint: enable=invalid-name

Methods for building a CP model.

Methods beginning with:

  • new_ create integer, boolean, or interval variables.
  • add_ create new constraints and add them to the model.
CpModel( model_proto: CpModelProto | None = None)
316    def __init__(self, model_proto: Optional[cmh.CpModelProto] = None) -> None:
317        cmh.CpBaseModel.__init__(self, model_proto)
318        self._add_pre_pep8_methods()

__init__(self: ortools.sat.python.cp_model_helper.CpBaseModel, arg0: operations_research::sat::CpModelProto) -> None

name: str
321    @property
322    def name(self) -> str:
323        """Returns the name of the model."""
324        if not self.model_proto or not self.model_proto.name:
325            return ""
326        return self.model_proto.name

Returns the name of the model.

def new_int_var( self, lb: int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, ub: int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, name: str) -> IntVar:
334    def new_int_var(self, lb: IntegralT, ub: IntegralT, name: str) -> IntVar:
335        """Create an integer variable with domain [lb, ub].
336
337        The CP-SAT solver is limited to integer variables. If you have fractional
338        values, scale them up so that they become integers; if you have strings,
339        encode them as integers.
340
341        Args:
342          lb: Lower bound for the variable.
343          ub: Upper bound for the variable.
344          name: The name of the variable.
345
346        Returns:
347          a variable whose domain is [lb, ub].
348        """
349        return (
350            IntVar(self.model_proto)
351            .with_name(name)
352            .with_domain(sorted_interval_list.Domain(lb, ub))
353        )

Create an integer variable with domain [lb, ub].

The CP-SAT solver is limited to integer variables. If you have fractional values, scale them up so that they become integers; if you have strings, encode them as integers.

Arguments:
  • lb: Lower bound for the variable.
  • ub: Upper bound for the variable.
  • name: The name of the variable.
Returns:

a variable whose domain is [lb, ub].

def new_int_var_from_domain( self, domain: Domain, name: str) -> IntVar:
355    def new_int_var_from_domain(
356        self, domain: sorted_interval_list.Domain, name: str
357    ) -> IntVar:
358        """Create an integer variable from a domain.
359
360        A domain is a set of integers specified by a collection of intervals.
361        For example, `model.new_int_var_from_domain(cp_model.
362             Domain.from_intervals([[1, 2], [4, 6]]), 'x')`
363
364        Args:
365          domain: An instance of the Domain class.
366          name: The name of the variable.
367
368        Returns:
369            a variable whose domain is the given domain.
370        """
371        return IntVar(self.model_proto).with_name(name).with_domain(domain)

Create an integer variable from a domain.

A domain is a set of integers specified by a collection of intervals. For example, model.new_int_var_from_domain(cp_model. Domain.from_intervals([[1, 2], [4, 6]]), 'x')

Arguments:
  • domain: An instance of the Domain class.
  • name: The name of the variable.
Returns:

a variable whose domain is the given domain.

def new_bool_var(self, name: str) -> IntVar:
373    def new_bool_var(self, name: str) -> IntVar:
374        """Creates a 0-1 variable with the given name."""
375        return (
376            IntVar(self.model_proto)
377            .with_name(name)
378            .with_domain(sorted_interval_list.Domain(0, 1))
379        )

Creates a 0-1 variable with the given name.

def new_constant( self, value: int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64) -> IntVar:
381    def new_constant(self, value: IntegralT) -> IntVar:
382        """Declares a constant integer."""
383        return IntVar(self.model_proto, self.get_or_make_index_from_constant(value))

Declares a constant integer.

def new_int_var_series( self, name: str, index: pandas.core.indexes.base.Index, lower_bounds: int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | pandas.core.series.Series, upper_bounds: int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | pandas.core.series.Series) -> pandas.core.series.Series:
385    def new_int_var_series(
386        self,
387        name: str,
388        index: pd.Index,
389        lower_bounds: Union[IntegralT, pd.Series],
390        upper_bounds: Union[IntegralT, pd.Series],
391    ) -> pd.Series:
392        """Creates a series of (scalar-valued) variables with the given name.
393
394        Args:
395          name (str): Required. The name of the variable set.
396          index (pd.Index): Required. The index to use for the variable set.
397          lower_bounds (Union[int, pd.Series]): A lower bound for variables in the
398            set. If a `pd.Series` is passed in, it will be based on the
399            corresponding values of the pd.Series.
400          upper_bounds (Union[int, pd.Series]): An upper bound for variables in the
401            set. If a `pd.Series` is passed in, it will be based on the
402            corresponding values of the pd.Series.
403
404        Returns:
405          pd.Series: The variable set indexed by its corresponding dimensions.
406
407        Raises:
408          TypeError: if the `index` is invalid (e.g. a `DataFrame`).
409          ValueError: if the `name` is not a valid identifier or already exists.
410          ValueError: if the `lowerbound` is greater than the `upperbound`.
411          ValueError: if the index of `lower_bound`, or `upper_bound` does not match
412          the input index.
413        """
414        if not isinstance(index, pd.Index):
415            raise TypeError("Non-index object is used as index")
416        if not name.isidentifier():
417            raise ValueError(f"name={name!r} is not a valid identifier")
418        if (
419            isinstance(lower_bounds, IntegralTypes)
420            and isinstance(upper_bounds, IntegralTypes)
421            and lower_bounds > upper_bounds
422        ):
423            raise ValueError(
424                f"lower_bound={lower_bounds} is greater than"
425                f" upper_bound={upper_bounds} for variable set={name}"
426            )
427
428        lower_bounds = _convert_to_series_and_validate_index(lower_bounds, index)
429        upper_bounds = _convert_to_series_and_validate_index(upper_bounds, index)
430        return pd.Series(
431            index=index,
432            data=[
433                # pylint: disable=g-complex-comprehension
434                IntVar(self.model_proto)
435                .with_name(f"{name}[{i}]")
436                .with_domain(
437                    sorted_interval_list.Domain(lower_bounds[i], upper_bounds[i])
438                )
439                for i in index
440            ],
441        )

Creates a series of (scalar-valued) variables with the given name.

Arguments:
  • name (str): Required. The name of the variable set.
  • index (pd.Index): Required. The index to use for the variable set.
  • lower_bounds (Union[int, pd.Series]): A lower bound for variables in the set. If a pd.Series is passed in, it will be based on the corresponding values of the pd.Series.
  • upper_bounds (Union[int, pd.Series]): An upper bound for variables in the set. If a pd.Series is passed in, it will be based on the corresponding values of the pd.Series.
Returns:

pd.Series: The variable set indexed by its corresponding dimensions.

Raises:
  • TypeError: if the index is invalid (e.g. a DataFrame).
  • ValueError: if the name is not a valid identifier or already exists.
  • ValueError: if the lowerbound is greater than the upperbound.
  • ValueError: if the index of lower_bound, or upper_bound does not match
  • the input index.
def new_bool_var_series( self, name: str, index: pandas.core.indexes.base.Index) -> pandas.core.series.Series:
443    def new_bool_var_series(
444        self,
445        name: str,
446        index: pd.Index,
447    ) -> pd.Series:
448        """Creates a series of (scalar-valued) variables with the given name.
449
450        Args:
451          name (str): Required. The name of the variable set.
452          index (pd.Index): Required. The index to use for the variable set.
453
454        Returns:
455          pd.Series: The variable set indexed by its corresponding dimensions.
456
457        Raises:
458          TypeError: if the `index` is invalid (e.g. a `DataFrame`).
459          ValueError: if the `name` is not a valid identifier or already exists.
460        """
461        if not isinstance(index, pd.Index):
462            raise TypeError("Non-index object is used as index")
463        if not name.isidentifier():
464            raise ValueError(f"name={name!r} is not a valid identifier")
465        return pd.Series(
466            index=index,
467            data=[
468                # pylint: disable=g-complex-comprehension
469                IntVar(self.model_proto)
470                .with_name(f"{name}[{i}]")
471                .with_domain(sorted_interval_list.Domain(0, 1))
472                for i in index
473            ],
474        )

Creates a series of (scalar-valued) variables with the given name.

Arguments:
  • name (str): Required. The name of the variable set.
  • index (pd.Index): Required. The index to use for the variable set.
Returns:

pd.Series: The variable set indexed by its corresponding dimensions.

Raises:
  • TypeError: if the index is invalid (e.g. a DataFrame).
  • ValueError: if the name is not a valid identifier or already exists.
def add_linear_constraint( self, linear_expr: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, lb: int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, ub: int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64) -> Constraint:
478    def add_linear_constraint(
479        self, linear_expr: LinearExprT, lb: IntegralT, ub: IntegralT
480    ) -> Constraint:
481        """Adds the constraint: `lb <= linear_expr <= ub`."""
482        return self.add_linear_expression_in_domain(
483            linear_expr, sorted_interval_list.Domain(lb, ub)
484        )

Adds the constraint: lb <= linear_expr <= ub.

def add_linear_expression_in_domain( self, linear_expr: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, domain: Domain) -> Constraint:
486    def add_linear_expression_in_domain(
487        self,
488        linear_expr: LinearExprT,
489        domain: sorted_interval_list.Domain,
490    ) -> Constraint:
491        """Adds the constraint: `linear_expr` in `domain`."""
492        if isinstance(linear_expr, LinearExpr):
493            ble = BoundedLinearExpression(linear_expr, domain)
494            if not ble.ok:
495                raise TypeError(
496                    "Cannot add a linear expression containing floating point"
497                    f" coefficients or constants: {type(linear_expr).__name__!r}"
498                )
499            return self._add_bounded_linear_expression(ble)
500        if isinstance(linear_expr, IntegralTypes):
501            if not domain.contains(int(linear_expr)):
502                return self.add_bool_or([])  # Evaluate to false.
503            else:
504                return self.add_bool_and([])  # Evaluate to true.
505        raise TypeError(
506            "not supported:"
507            f" CpModel.add_linear_expression_in_domain({type(linear_expr).__name__!r})"
508        )

Adds the constraint: linear_expr in domain.

def add( self, ct: BoundedLinearExpression | bool | numpy.bool) -> Constraint:
510    def add(self, ct: Union[BoundedLinearExpression, bool, np.bool_]) -> Constraint:
511        """Adds a `BoundedLinearExpression` to the model.
512
513        Args:
514          ct: A [`BoundedLinearExpression`](#boundedlinearexpression).
515
516        Returns:
517          An instance of the `Constraint` class.
518
519        Raises:
520          TypeError: If the `ct` is not a `BoundedLinearExpression` or a Boolean.
521        """
522        if isinstance(ct, BoundedLinearExpression):
523            return self._add_bounded_linear_expression(ct)
524        if ct and self.is_boolean_value(ct):
525            return self.add_bool_or([True])
526        if not ct and self.is_boolean_value(ct):
527            return self.add_bool_or([])  # Evaluate to false.
528        raise TypeError(f"not supported: CpModel.add({type(ct).__name__!r})")

Adds a BoundedLinearExpression to the model.

Arguments:
Returns:

An instance of the Constraint class.

Raises:
def add_all_different(self, *expressions):
538    def add_all_different(self, *expressions):
539        """Adds AllDifferent(expressions).
540
541        This constraint forces all expressions to have different values.
542
543        Args:
544          *expressions: simple expressions of the form a * var + constant.
545
546        Returns:
547          An instance of the `Constraint` class.
548        """
549        return self._add_all_different(*expressions)

Adds AllDifferent(expressions).

This constraint forces all expressions to have different values.

Arguments:
  • *expressions: simple expressions of the form a * var + constant.
Returns:

An instance of the Constraint class.

def add_element( self, index: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, expressions: Sequence[LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64], target: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64) -> Constraint:
551    def add_element(
552        self,
553        index: LinearExprT,
554        expressions: Sequence[LinearExprT],
555        target: LinearExprT,
556    ) -> Constraint:
557        """Adds the element constraint: `expressions[index] == target`.
558
559        Args:
560          index: The index of the selected expression in the array. It must be an
561            affine expression (a * var + b).
562          expressions: A list of affine expressions.
563          target: The expression constrained to be equal to the selected expression.
564            It must be an affine expression (a * var + b).
565
566        Returns:
567          An instance of the `Constraint` class.
568        """
569
570        if not expressions:
571            raise ValueError("add_element expects a non-empty expressions array")
572
573        if isinstance(index, IntegralTypes):
574            expression: LinearExprT = list(expressions)[int(index)]
575            return self.add(expression == target)
576
577        return self._add_element(index, expressions, target)

Adds the element constraint: expressions[index] == target.

Arguments:
  • index: The index of the selected expression in the array. It must be an affine expression (a * var + b).
  • expressions: A list of affine expressions.
  • target: The expression constrained to be equal to the selected expression. It must be an affine expression (a * var + b).
Returns:

An instance of the Constraint class.

def add_circuit( self, arcs: Sequence[tuple[int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool]]) -> Constraint:
579    def add_circuit(self, arcs: Sequence[ArcT]) -> Constraint:
580        """Adds Circuit(arcs).
581
582        Adds a circuit constraint from a sparse list of arcs that encode the graph.
583
584        A circuit is a unique Hamiltonian cycle in a subgraph of the total
585        graph. In case a node 'i' is not in the cycle, then there must be a
586        loop arc 'i -> i' associated with a true literal. Otherwise
587        this constraint will fail.
588
589        Args:
590          arcs: a list of arcs. An arc is a tuple (source_node, destination_node,
591            literal). The arc is selected in the circuit if the literal is true.
592            Both source_node and destination_node must be integers between 0 and the
593            number of nodes - 1.
594
595        Returns:
596          An instance of the `Constraint` class.
597
598        Raises:
599          ValueError: If the list of arcs is empty.
600        """
601        if not arcs:
602            raise ValueError("add_circuit expects a non-empty array of arcs")
603        return self._add_circuit(arcs)

Adds Circuit(arcs).

Adds a circuit constraint from a sparse list of arcs that encode the graph.

A circuit is a unique Hamiltonian cycle in a subgraph of the total graph. In case a node 'i' is not in the cycle, then there must be a loop arc 'i -> i' associated with a true literal. Otherwise this constraint will fail.

Arguments:
  • arcs: a list of arcs. An arc is a tuple (source_node, destination_node, literal). The arc is selected in the circuit if the literal is true. Both source_node and destination_node must be integers between 0 and the number of nodes - 1.
Returns:

An instance of the Constraint class.

Raises:
  • ValueError: If the list of arcs is empty.
def add_multiple_circuit( self, arcs: Sequence[tuple[int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool]]) -> Constraint:
605    def add_multiple_circuit(self, arcs: Sequence[ArcT]) -> Constraint:
606        """Adds a multiple circuit constraint, aka the 'VRP' constraint.
607
608        The direct graph where arc #i (from tails[i] to head[i]) is present iff
609        literals[i] is true must satisfy this set of properties:
610        - #incoming arcs == 1 except for node 0.
611        - #outgoing arcs == 1 except for node 0.
612        - for node zero, #incoming arcs == #outgoing arcs.
613        - There are no duplicate arcs.
614        - Self-arcs are allowed except for node 0.
615        - There is no cycle in this graph, except through node 0.
616
617        Args:
618          arcs: a list of arcs. An arc is a tuple (source_node, destination_node,
619            literal). The arc is selected in the circuit if the literal is true.
620            Both source_node and destination_node must be integers between 0 and the
621            number of nodes - 1.
622
623        Returns:
624          An instance of the `Constraint` class.
625
626        Raises:
627          ValueError: If the list of arcs is empty.
628        """
629        if not arcs:
630            raise ValueError("add_multiple_circuit expects a non-empty array of arcs")
631        return self._add_routes(arcs)

Adds a multiple circuit constraint, aka the 'VRP' constraint.

The direct graph where arc #i (from tails[i] to head[i]) is present iff literals[i] is true must satisfy this set of properties:

  • #incoming arcs == 1 except for node 0.
  • #outgoing arcs == 1 except for node 0.
  • for node zero, #incoming arcs == #outgoing arcs.
  • There are no duplicate arcs.
  • Self-arcs are allowed except for node 0.
  • There is no cycle in this graph, except through node 0.
Arguments:
  • arcs: a list of arcs. An arc is a tuple (source_node, destination_node, literal). The arc is selected in the circuit if the literal is true. Both source_node and destination_node must be integers between 0 and the number of nodes - 1.
Returns:

An instance of the Constraint class.

Raises:
  • ValueError: If the list of arcs is empty.
def add_allowed_assignments( self, expressions: Sequence[LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64], tuples_list: Iterable[Sequence[int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64]]) -> Constraint:
633    def add_allowed_assignments(
634        self,
635        expressions: Sequence[LinearExprT],
636        tuples_list: Iterable[Sequence[IntegralT]],
637    ) -> Constraint:
638        """Adds AllowedAssignments(expressions, tuples_list).
639
640        An AllowedAssignments constraint is a constraint on an array of affine
641        expressions, which requires that when all expressions are assigned values,
642        the
643        resulting array equals one of the  tuples in `tuple_list`.
644
645        Args:
646          expressions: A list of affine expressions (a * var + b).
647          tuples_list: A list of admissible tuples. Each tuple must have the same
648            length as the expressions, and the ith value of a tuple corresponds to
649            the ith expression.
650
651        Returns:
652          An instance of the `Constraint` class.
653
654        Raises:
655          TypeError: If a tuple does not have the same size as the list of
656              expressions.
657          ValueError: If the array of expressions is empty.
658        """
659
660        if not expressions:
661            raise ValueError(
662                "add_allowed_assignments expects a non-empty expressions array"
663            )
664
665        return self._add_table(expressions, tuples_list, False)

Adds AllowedAssignments(expressions, tuples_list).

An AllowedAssignments constraint is a constraint on an array of affine expressions, which requires that when all expressions are assigned values, the resulting array equals one of the tuples in tuple_list.

Arguments:
  • expressions: A list of affine expressions (a * var + b).
  • tuples_list: A list of admissible tuples. Each tuple must have the same length as the expressions, and the ith value of a tuple corresponds to the ith expression.
Returns:

An instance of the Constraint class.

Raises:
  • TypeError: If a tuple does not have the same size as the list of expressions.
  • ValueError: If the array of expressions is empty.
def add_forbidden_assignments( self, expressions: Sequence[LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64], tuples_list: Iterable[Sequence[int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64]]) -> Constraint:
667    def add_forbidden_assignments(
668        self,
669        expressions: Sequence[LinearExprT],
670        tuples_list: Iterable[Sequence[IntegralT]],
671    ) -> Constraint:
672        """Adds add_forbidden_assignments(expressions, [tuples_list]).
673
674        A ForbiddenAssignments constraint is a constraint on an array of affine
675        expressions where the list of impossible combinations is provided in the
676        tuples list.
677
678        Args:
679          expressions: A list of affine expressions (a * var + b).
680          tuples_list: A list of forbidden tuples. Each tuple must have the same
681            length as the expressions, and the *i*th value of a tuple corresponds to
682            the *i*th expression.
683
684        Returns:
685          An instance of the `Constraint` class.
686
687        Raises:
688          TypeError: If a tuple does not have the same size as the list of
689                     expressions.
690          ValueError: If the array of expressions is empty.
691        """
692
693        if not expressions:
694            raise ValueError(
695                "add_forbidden_assignments expects a non-empty expressions array"
696            )
697
698        return self._add_table(expressions, tuples_list, True)

Adds add_forbidden_assignments(expressions, [tuples_list]).

A ForbiddenAssignments constraint is a constraint on an array of affine expressions where the list of impossible combinations is provided in the tuples list.

Arguments:
  • expressions: A list of affine expressions (a * var + b).
  • tuples_list: A list of forbidden tuples. Each tuple must have the same length as the expressions, and the ith value of a tuple corresponds to the ith expression.
Returns:

An instance of the Constraint class.

Raises:
  • TypeError: If a tuple does not have the same size as the list of expressions.
  • ValueError: If the array of expressions is empty.
def add_automaton( self, transition_expressions: Sequence[LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64], starting_state: int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, final_states: Sequence[int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64], transition_triples: Sequence[tuple[int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64]]) -> Constraint:
700    def add_automaton(
701        self,
702        transition_expressions: Sequence[LinearExprT],
703        starting_state: IntegralT,
704        final_states: Sequence[IntegralT],
705        transition_triples: Sequence[tuple[IntegralT, IntegralT, IntegralT]],
706    ) -> Constraint:
707        """Adds an automaton constraint.
708
709        An automaton constraint takes a list of affine expressions (a * var + b) (of
710        size *n*), an initial state, a set of final states, and a set of
711        transitions. A transition is a triplet (*tail*, *transition*, *head*), where
712        *tail* and *head* are states, and *transition* is the label of an arc from
713        *head* to *tail*, corresponding to the value of one expression in the list
714        of
715        expressions.
716
717        This automaton will be unrolled into a flow with *n* + 1 phases. Each phase
718        contains the possible states of the automaton. The first state contains the
719        initial state. The last phase contains the final states.
720
721        Between two consecutive phases *i* and *i* + 1, the automaton creates a set
722        of arcs. For each transition (*tail*, *transition*, *head*), it will add
723        an arc from the state *tail* of phase *i* and the state *head* of phase
724        *i* + 1. This arc is labeled by the value *transition* of the expression
725        `expressions[i]`. That is, this arc can only be selected if `expressions[i]`
726        is assigned the value *transition*.
727
728        A feasible solution of this constraint is an assignment of expressions such
729        that, starting from the initial state in phase 0, there is a path labeled by
730        the values of the expressions that ends in one of the final states in the
731        final phase.
732
733        Args:
734          transition_expressions: A non-empty list of affine expressions (a * var +
735            b) whose values correspond to the labels of the arcs traversed by the
736            automaton.
737          starting_state: The initial state of the automaton.
738          final_states: A non-empty list of admissible final states.
739          transition_triples: A list of transitions for the automaton, in the
740            following format (current_state, variable_value, next_state).
741
742        Returns:
743          An instance of the `Constraint` class.
744
745        Raises:
746          ValueError: if `transition_expressions`, `final_states`, or
747            `transition_triples` are empty.
748        """
749
750        if not transition_expressions:
751            raise ValueError(
752                "add_automaton expects a non-empty transition_expressions array"
753            )
754        if not final_states:
755            raise ValueError("add_automaton expects some final states")
756
757        if not transition_triples:
758            raise ValueError("add_automaton expects some transition triples")
759
760        return self._add_automaton(
761            transition_expressions,
762            starting_state,
763            final_states,
764            transition_triples,
765        )

Adds an automaton constraint.

An automaton constraint takes a list of affine expressions (a * var + b) (of size n), an initial state, a set of final states, and a set of transitions. A transition is a triplet (tail, transition, head), where tail and head are states, and transition is the label of an arc from head to tail, corresponding to the value of one expression in the list of expressions.

This automaton will be unrolled into a flow with n + 1 phases. Each phase contains the possible states of the automaton. The first state contains the initial state. The last phase contains the final states.

Between two consecutive phases i and i + 1, the automaton creates a set of arcs. For each transition (tail, transition, head), it will add an arc from the state tail of phase i and the state head of phase i + 1. This arc is labeled by the value transition of the expression expressions[i]. That is, this arc can only be selected if expressions[i] is assigned the value transition.

A feasible solution of this constraint is an assignment of expressions such that, starting from the initial state in phase 0, there is a path labeled by the values of the expressions that ends in one of the final states in the final phase.

Arguments:
  • transition_expressions: A non-empty list of affine expressions (a * var + b) whose values correspond to the labels of the arcs traversed by the automaton.
  • starting_state: The initial state of the automaton.
  • final_states: A non-empty list of admissible final states.
  • transition_triples: A list of transitions for the automaton, in the following format (current_state, variable_value, next_state).
Returns:

An instance of the Constraint class.

Raises:
  • ValueError: if transition_expressions, final_states, or transition_triples are empty.
def add_inverse( self, variables: Sequence[IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64], inverse_variables: Sequence[IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64]) -> Constraint:
767    def add_inverse(
768        self,
769        variables: Sequence[VariableT],
770        inverse_variables: Sequence[VariableT],
771    ) -> Constraint:
772        """Adds Inverse(variables, inverse_variables).
773
774        An inverse constraint enforces that if `variables[i]` is assigned a value
775        `j`, then `inverse_variables[j]` is assigned a value `i`. And vice versa.
776
777        Args:
778          variables: An array of integer variables.
779          inverse_variables: An array of integer variables.
780
781        Returns:
782          An instance of the `Constraint` class.
783
784        Raises:
785          TypeError: if variables and inverse_variables have different lengths, or
786              if they are empty.
787        """
788
789        if not variables or not inverse_variables:
790            raise TypeError("The Inverse constraint does not accept empty arrays")
791        if len(variables) != len(inverse_variables):
792            raise TypeError(
793                "In the inverse constraint, the two array variables and"
794                " inverse_variables must have the same length."
795            )
796        return self._add_inverse(variables, inverse_variables)

Adds Inverse(variables, inverse_variables).

An inverse constraint enforces that if variables[i] is assigned a value j, then inverse_variables[j] is assigned a value i. And vice versa.

Arguments:
  • variables: An array of integer variables.
  • inverse_variables: An array of integer variables.
Returns:

An instance of the Constraint class.

Raises:
  • TypeError: if variables and inverse_variables have different lengths, or if they are empty.
def add_reservoir_constraint( self, times: Sequence[LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64], level_changes: Sequence[LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64], min_level: int, max_level: int) -> Constraint:
798    def add_reservoir_constraint(
799        self,
800        times: Sequence[LinearExprT],
801        level_changes: Sequence[LinearExprT],
802        min_level: int,
803        max_level: int,
804    ) -> Constraint:
805        """Adds Reservoir(times, level_changes, min_level, max_level).
806
807        Maintains a reservoir level within bounds. The water level starts at 0, and
808        at any time, it must be between min_level and max_level.
809
810        If the affine expression `times[i]` is assigned a value t, then the current
811        level changes by `level_changes[i]`, which is constant, at time t.
812
813         Note that min level must be <= 0, and the max level must be >= 0. Please
814         use fixed level_changes to simulate initial state.
815
816         Therefore, at any time:
817             sum(level_changes[i] if times[i] <= t) in [min_level, max_level]
818
819        Args:
820          times: A list of 1-var affine expressions (a * x + b) which specify the
821            time of the filling or emptying the reservoir.
822          level_changes: A list of integer values that specifies the amount of the
823            emptying or filling. Currently, variable demands are not supported.
824          min_level: At any time, the level of the reservoir must be greater or
825            equal than the min level.
826          max_level: At any time, the level of the reservoir must be less or equal
827            than the max level.
828
829        Returns:
830          An instance of the `Constraint` class.
831
832        Raises:
833          ValueError: if max_level < min_level.
834
835          ValueError: if max_level < 0.
836
837          ValueError: if min_level > 0
838        """
839
840        return self._add_reservoir(
841            times,
842            level_changes,
843            [],
844            min_level,
845            max_level,
846        )

Adds Reservoir(times, level_changes, min_level, max_level).

Maintains a reservoir level within bounds. The water level starts at 0, and at any time, it must be between min_level and max_level.

If the affine expression times[i] is assigned a value t, then the current level changes by level_changes[i], which is constant, at time t.

Note that min level must be <= 0, and the max level must be >= 0. Please use fixed level_changes to simulate initial state.

Therefore, at any time: sum(level_changes[i] if times[i] <= t) in [min_level, max_level]

Arguments:
  • times: A list of 1-var affine expressions (a * x + b) which specify the time of the filling or emptying the reservoir.
  • level_changes: A list of integer values that specifies the amount of the emptying or filling. Currently, variable demands are not supported.
  • min_level: At any time, the level of the reservoir must be greater or equal than the min level.
  • max_level: At any time, the level of the reservoir must be less or equal than the max level.
Returns:

An instance of the Constraint class.

Raises:
  • ValueError: if max_level < min_level.
  • ValueError: if max_level < 0.
  • ValueError: if min_level > 0
def add_reservoir_constraint_with_active( self, times: Sequence[LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64], level_changes: Sequence[LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64], actives: Sequence[ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool], min_level: int, max_level: int) -> Constraint:
848    def add_reservoir_constraint_with_active(
849        self,
850        times: Sequence[LinearExprT],
851        level_changes: Sequence[LinearExprT],
852        actives: Sequence[LiteralT],
853        min_level: int,
854        max_level: int,
855    ) -> Constraint:
856        """Adds Reservoir(times, level_changes, actives, min_level, max_level).
857
858        Maintains a reservoir level within bounds. The water level starts at 0, and
859        at any time, it must be between min_level and max_level.
860
861        If the variable `times[i]` is assigned a value t, and `actives[i]` is
862        `True`, then the current level changes by `level_changes[i]`, which is
863        constant,
864        at time t.
865
866         Note that min level must be <= 0, and the max level must be >= 0. Please
867         use fixed level_changes to simulate initial state.
868
869         Therefore, at any time:
870             sum(level_changes[i] * actives[i] if times[i] <= t) in [min_level,
871             max_level]
872
873
874        The array of boolean variables 'actives', if defined, indicates which
875        actions are actually performed.
876
877        Args:
878          times: A list of 1-var affine expressions (a * x + b) which specify the
879            time of the filling or emptying the reservoir.
880          level_changes: A list of integer values that specifies the amount of the
881            emptying or filling. Currently, variable demands are not supported.
882          actives: a list of boolean variables. They indicates if the
883            emptying/refilling events actually take place.
884          min_level: At any time, the level of the reservoir must be greater or
885            equal than the min level.
886          max_level: At any time, the level of the reservoir must be less or equal
887            than the max level.
888
889        Returns:
890          An instance of the `Constraint` class.
891
892        Raises:
893          ValueError: if max_level < min_level.
894
895          ValueError: if max_level < 0.
896
897          ValueError: if min_level > 0
898        """
899
900        if max_level < min_level:
901            raise ValueError("Reservoir constraint must have a max_level >= min_level")
902
903        if max_level < 0:
904            raise ValueError("Reservoir constraint must have a max_level >= 0")
905
906        if min_level > 0:
907            raise ValueError("Reservoir constraint must have a min_level <= 0")
908
909        if not times:
910            raise ValueError("Reservoir constraint must have a non-empty times array")
911
912        return self._add_reservoir(
913            times,
914            level_changes,
915            actives,
916            min_level,
917            max_level,
918        )

Adds Reservoir(times, level_changes, actives, min_level, max_level).

Maintains a reservoir level within bounds. The water level starts at 0, and at any time, it must be between min_level and max_level.

If the variable times[i] is assigned a value t, and actives[i] is True, then the current level changes by level_changes[i], which is constant, at time t.

Note that min level must be <= 0, and the max level must be >= 0. Please use fixed level_changes to simulate initial state.

Therefore, at any time: sum(level_changes[i] * actives[i] if times[i] <= t) in [min_level, max_level]

The array of boolean variables 'actives', if defined, indicates which actions are actually performed.

Arguments:
  • times: A list of 1-var affine expressions (a * x + b) which specify the time of the filling or emptying the reservoir.
  • level_changes: A list of integer values that specifies the amount of the emptying or filling. Currently, variable demands are not supported.
  • actives: a list of boolean variables. They indicates if the emptying/refilling events actually take place.
  • min_level: At any time, the level of the reservoir must be greater or equal than the min level.
  • max_level: At any time, the level of the reservoir must be less or equal than the max level.
Returns:

An instance of the Constraint class.

Raises:
  • ValueError: if max_level < min_level.
  • ValueError: if max_level < 0.
  • ValueError: if min_level > 0
def add_map_domain( self, var: IntVar, bool_var_array: Iterable[IntVar], offset: int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 = 0):
920    def add_map_domain(
921        self, var: IntVar, bool_var_array: Iterable[IntVar], offset: IntegralT = 0
922    ):
923        """Adds `var == i + offset <=> bool_var_array[i] == true for all i`."""
924        for i, bool_var in enumerate(bool_var_array):
925            self.add(var == i + offset).only_enforce_if(bool_var)
926            self.add(var != i + offset).only_enforce_if(~bool_var)

Adds var == i + offset <=> bool_var_array[i] == true for all i.

def add_implication( self, a: ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool, b: ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool) -> Constraint:
928    def add_implication(self, a: LiteralT, b: LiteralT) -> Constraint:
929        """Adds `a => b` (`a` implies `b`)."""
930        return self.add_bool_and(b).only_enforce_if(a)

Adds a => b (a implies b).

def add_bool_or(self, *literals):
938    def add_bool_or(self, *literals):
939        """Adds `Or(literals) == true`: sum(literals) >= 1."""
940        return self._add_bool_argument_constraint(
941            cmh.BoolArgumentConstraint.bool_or, *literals
942        )

Adds Or(literals) == true: sum(literals) >= 1.

def add_at_least_one(self, *literals):
950    def add_at_least_one(self, *literals):
951        """Same as `add_bool_or`: `sum(literals) >= 1`."""
952        return self._add_bool_argument_constraint(
953            cmh.BoolArgumentConstraint.bool_or, *literals
954        )

Same as add_bool_or: sum(literals) >= 1.

def add_at_most_one(self, *literals) -> Constraint:
962    def add_at_most_one(self, *literals) -> Constraint:
963        """Adds `AtMostOne(literals)`: `sum(literals) <= 1`."""
964        return self._add_bool_argument_constraint(
965            cmh.BoolArgumentConstraint.at_most_one, *literals
966        )

Adds AtMostOne(literals): sum(literals) <= 1.

def add_exactly_one(self, *literals):
974    def add_exactly_one(self, *literals):
975        """Adds `ExactlyOne(literals)`: `sum(literals) == 1`."""
976        return self._add_bool_argument_constraint(
977            cmh.BoolArgumentConstraint.exactly_one, *literals
978        )

Adds ExactlyOne(literals): sum(literals) == 1.

def add_bool_and(self, *literals):
986    def add_bool_and(self, *literals):
987        """Adds `And(literals) == true`."""
988        return self._add_bool_argument_constraint(
989            cmh.BoolArgumentConstraint.bool_and, *literals
990        )

Adds And(literals) == true.

def add_bool_xor(self, *literals):
 998    def add_bool_xor(self, *literals):
 999        """Adds `XOr(literals) == true`.
1000
1001        In contrast to add_bool_or and add_bool_and, it does not support
1002            .only_enforce_if().
1003
1004        Args:
1005          *literals: the list of literals in the constraint.
1006
1007        Returns:
1008          An `Constraint` object.
1009        """
1010        return self._add_bool_argument_constraint(
1011            cmh.BoolArgumentConstraint.bool_xor, *literals
1012        )

Adds XOr(literals) == true.

In contrast to add_bool_or and add_bool_and, it does not support .only_enforce_if().

Arguments:
  • *literals: the list of literals in the constraint.
Returns:

An Constraint object.

def add_min_equality( self, target, *expressions) -> Constraint:
1024    def add_min_equality(self, target, *expressions) -> Constraint:
1025        """Adds `target == Min(expressions)`."""
1026        return self._add_linear_argument_constraint(
1027            cmh.LinearArgumentConstraint.min, target, *expressions
1028        )

Adds target == Min(expressions).

def add_max_equality( self, target, *expressions) -> Constraint:
1040    def add_max_equality(self, target, *expressions) -> Constraint:
1041        """Adds `target == Max(expressions)`."""
1042        return self._add_linear_argument_constraint(
1043            cmh.LinearArgumentConstraint.max, target, *expressions
1044        )

Adds target == Max(expressions).

def add_division_equality( self, target: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, num: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, denom: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64) -> Constraint:
1046    def add_division_equality(
1047        self, target: LinearExprT, num: LinearExprT, denom: LinearExprT
1048    ) -> Constraint:
1049        """Adds `target == num // denom` (integer division rounded towards 0)."""
1050        return self._add_linear_argument_constraint(
1051            cmh.LinearArgumentConstraint.div, target, [num, denom]
1052        )

Adds target == num // denom (integer division rounded towards 0).

def add_abs_equality( self, target: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, expr: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64) -> Constraint:
1054    def add_abs_equality(self, target: LinearExprT, expr: LinearExprT) -> Constraint:
1055        """Adds `target == Abs(expr)`."""
1056        return self._add_linear_argument_constraint(
1057            cmh.LinearArgumentConstraint.max, target, [expr, -expr]
1058        )

Adds target == Abs(expr).

def add_modulo_equality( self, target: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, expr: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, mod: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64) -> Constraint:
1060    def add_modulo_equality(
1061        self, target: LinearExprT, expr: LinearExprT, mod: LinearExprT
1062    ) -> Constraint:
1063        """Adds `target = expr % mod`.
1064
1065        It uses the C convention, that is the result is the remainder of the
1066        integral division rounded towards 0.
1067
1068            For example:
1069            * 10 % 3 = 1
1070            * -10 % 3 = -1
1071            * 10 % -3 = 1
1072            * -10 % -3 = -1
1073
1074        Args:
1075          target: the target expression.
1076          expr: the expression to compute the modulo of.
1077          mod: the modulus expression.
1078
1079        Returns:
1080          A `Constraint` object.
1081        """
1082        return self._add_linear_argument_constraint(
1083            cmh.LinearArgumentConstraint.mod, target, [expr, mod]
1084        )

Adds target = expr % mod.

It uses the C convention, that is the result is the remainder of the integral division rounded towards 0.

For example:
* 10 % 3 = 1
* -10 % 3 = -1
* 10 % -3 = 1
* -10 % -3 = -1
Arguments:
  • target: the target expression.
  • expr: the expression to compute the modulo of.
  • mod: the modulus expression.
Returns:

A Constraint object.

def add_multiplication_equality( self, target: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, *expressions: Iterable[LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64] | LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64) -> Constraint:
1086    def add_multiplication_equality(
1087        self,
1088        target: LinearExprT,
1089        *expressions: Union[Iterable[LinearExprT], LinearExprT],
1090    ) -> Constraint:
1091        """Adds `target == expressions[0] * .. * expressions[n]`."""
1092        return self._add_linear_argument_constraint(
1093            cmh.LinearArgumentConstraint.prod, target, *expressions
1094        )

Adds target == expressions[0] * .. * expressions[n].

def new_interval_var( self, start: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, size: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, end: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, name: str) -> IntervalVar:
1098    def new_interval_var(
1099        self, start: LinearExprT, size: LinearExprT, end: LinearExprT, name: str
1100    ) -> IntervalVar:
1101        """Creates an interval variable from start, size, and end.
1102
1103        An interval variable is a constraint, that is itself used in other
1104        constraints like NoOverlap.
1105
1106        Internally, it ensures that `start + size == end`.
1107
1108        Args:
1109          start: The start of the interval. It must be of the form a * var + b.
1110          size: The size of the interval. It must be of the form a * var + b.
1111          end: The end of the interval. It must be of the form a * var + b.
1112          name: The name of the interval variable.
1113
1114        Returns:
1115          An `IntervalVar` object.
1116        """
1117        return self._new_interval_var(name, start, size, end, [])

Creates an interval variable from start, size, and end.

An interval variable is a constraint, that is itself used in other constraints like NoOverlap.

Internally, it ensures that start + size == end.

Arguments:
  • start: The start of the interval. It must be of the form a * var + b.
  • size: The size of the interval. It must be of the form a * var + b.
  • end: The end of the interval. It must be of the form a * var + b.
  • name: The name of the interval variable.
Returns:

An IntervalVar object.

def new_interval_var_series( self, name: str, index: pandas.core.indexes.base.Index, starts: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | pandas.core.series.Series, sizes: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | pandas.core.series.Series, ends: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | pandas.core.series.Series) -> pandas.core.series.Series:
1119    def new_interval_var_series(
1120        self,
1121        name: str,
1122        index: pd.Index,
1123        starts: Union[LinearExprT, pd.Series],
1124        sizes: Union[LinearExprT, pd.Series],
1125        ends: Union[LinearExprT, pd.Series],
1126    ) -> pd.Series:
1127        """Creates a series of interval variables with the given name.
1128
1129        Args:
1130          name (str): Required. The name of the variable set.
1131          index (pd.Index): Required. The index to use for the variable set.
1132          starts (Union[LinearExprT, pd.Series]): The start of each interval in the
1133            set. If a `pd.Series` is passed in, it will be based on the
1134            corresponding values of the pd.Series.
1135          sizes (Union[LinearExprT, pd.Series]): The size of each interval in the
1136            set. If a `pd.Series` is passed in, it will be based on the
1137            corresponding values of the pd.Series.
1138          ends (Union[LinearExprT, pd.Series]): The ends of each interval in the
1139            set. If a `pd.Series` is passed in, it will be based on the
1140            corresponding values of the pd.Series.
1141
1142        Returns:
1143          pd.Series: The interval variable set indexed by its corresponding
1144          dimensions.
1145
1146        Raises:
1147          TypeError: if the `index` is invalid (e.g. a `DataFrame`).
1148          ValueError: if the `name` is not a valid identifier or already exists.
1149          ValueError: if the all the indexes do not match.
1150        """
1151        if not isinstance(index, pd.Index):
1152            raise TypeError("Non-index object is used as index")
1153        if not name.isidentifier():
1154            raise ValueError(f"name={name!r} is not a valid identifier")
1155
1156        starts = _convert_to_series_and_validate_index(starts, index)
1157        sizes = _convert_to_series_and_validate_index(sizes, index)
1158        ends = _convert_to_series_and_validate_index(ends, index)
1159        interval_array = []
1160        for i in index:
1161            interval_array.append(
1162                self.new_interval_var(
1163                    start=starts[i],
1164                    size=sizes[i],
1165                    end=ends[i],
1166                    name=f"{name}[{i}]",
1167                )
1168            )
1169        return pd.Series(index=index, data=interval_array)

Creates a series of interval variables with the given name.

Arguments:
  • name (str): Required. The name of the variable set.
  • index (pd.Index): Required. The index to use for the variable set.
  • starts (Union[LinearExprT, pd.Series]): The start of each interval in the set. If a pd.Series is passed in, it will be based on the corresponding values of the pd.Series.
  • sizes (Union[LinearExprT, pd.Series]): The size of each interval in the set. If a pd.Series is passed in, it will be based on the corresponding values of the pd.Series.
  • ends (Union[LinearExprT, pd.Series]): The ends of each interval in the set. If a pd.Series is passed in, it will be based on the corresponding values of the pd.Series.
Returns:

pd.Series: The interval variable set indexed by its corresponding dimensions.

Raises:
  • TypeError: if the index is invalid (e.g. a DataFrame).
  • ValueError: if the name is not a valid identifier or already exists.
  • ValueError: if the all the indexes do not match.
def new_fixed_size_interval_var( self, start: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, size: int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, name: str) -> IntervalVar:
1171    def new_fixed_size_interval_var(
1172        self, start: LinearExprT, size: IntegralT, name: str
1173    ) -> IntervalVar:
1174        """Creates an interval variable from start, and a fixed size.
1175
1176        An interval variable is a constraint, that is itself used in other
1177        constraints like NoOverlap.
1178
1179        Args:
1180          start: The start of the interval. It must be of the form a * var + b.
1181          size: The size of the interval. It must be an integer value.
1182          name: The name of the interval variable.
1183
1184        Returns:
1185          An `IntervalVar` object.
1186        """
1187        return self._new_interval_var(name, start, size, start + size, [])

Creates an interval variable from start, and a fixed size.

An interval variable is a constraint, that is itself used in other constraints like NoOverlap.

Arguments:
  • start: The start of the interval. It must be of the form a * var + b.
  • size: The size of the interval. It must be an integer value.
  • name: The name of the interval variable.
Returns:

An IntervalVar object.

def new_fixed_size_interval_var_series( self, name: str, index: pandas.core.indexes.base.Index, starts: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | pandas.core.series.Series, sizes: int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | pandas.core.series.Series) -> pandas.core.series.Series:
1189    def new_fixed_size_interval_var_series(
1190        self,
1191        name: str,
1192        index: pd.Index,
1193        starts: Union[LinearExprT, pd.Series],
1194        sizes: Union[IntegralT, pd.Series],
1195    ) -> pd.Series:
1196        """Creates a series of interval variables with the given name.
1197
1198        Args:
1199          name (str): Required. The name of the variable set.
1200          index (pd.Index): Required. The index to use for the variable set.
1201          starts (Union[LinearExprT, pd.Series]): The start of each interval in the
1202            set. If a `pd.Series` is passed in, it will be based on the
1203            corresponding values of the pd.Series.
1204          sizes (Union[IntegralT, pd.Series]): The fixed size of each interval in
1205            the set. If a `pd.Series` is passed in, it will be based on the
1206            corresponding values of the pd.Series.
1207
1208        Returns:
1209          pd.Series: The interval variable set indexed by its corresponding
1210          dimensions.
1211
1212        Raises:
1213          TypeError: if the `index` is invalid (e.g. a `DataFrame`).
1214          ValueError: if the `name` is not a valid identifier or already exists.
1215          ValueError: if the all the indexes do not match.
1216        """
1217        if not isinstance(index, pd.Index):
1218            raise TypeError("Non-index object is used as index")
1219        if not name.isidentifier():
1220            raise ValueError(f"name={name!r} is not a valid identifier")
1221
1222        starts = _convert_to_series_and_validate_index(starts, index)
1223        sizes = _convert_to_series_and_validate_index(sizes, index)
1224        interval_array = []
1225        for i in index:
1226            interval_array.append(
1227                self.new_fixed_size_interval_var(
1228                    start=starts[i],
1229                    size=sizes[i],
1230                    name=f"{name}[{i}]",
1231                )
1232            )
1233        return pd.Series(index=index, data=interval_array)

Creates a series of interval variables with the given name.

Arguments:
  • name (str): Required. The name of the variable set.
  • index (pd.Index): Required. The index to use for the variable set.
  • starts (Union[LinearExprT, pd.Series]): The start of each interval in the set. If a pd.Series is passed in, it will be based on the corresponding values of the pd.Series.
  • sizes (Union[IntegralT, pd.Series]): The fixed size of each interval in the set. If a pd.Series is passed in, it will be based on the corresponding values of the pd.Series.
Returns:

pd.Series: The interval variable set indexed by its corresponding dimensions.

Raises:
  • TypeError: if the index is invalid (e.g. a DataFrame).
  • ValueError: if the name is not a valid identifier or already exists.
  • ValueError: if the all the indexes do not match.
def new_optional_interval_var( self, start: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, size: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, end: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, is_present: ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool, name: str) -> IntervalVar:
1235    def new_optional_interval_var(
1236        self,
1237        start: LinearExprT,
1238        size: LinearExprT,
1239        end: LinearExprT,
1240        is_present: LiteralT,
1241        name: str,
1242    ) -> IntervalVar:
1243        """Creates an optional interval var from start, size, end, and is_present.
1244
1245        An optional interval variable is a constraint, that is itself used in other
1246        constraints like NoOverlap. This constraint is protected by a presence
1247        literal that indicates if it is active or not.
1248
1249        Internally, it ensures that `is_present` implies `start + size ==
1250        end`.
1251
1252        Args:
1253          start: The start of the interval. It must be of the form a * var + b.
1254          size: The size of the interval. It must be of the form a * var + b.
1255          end: The end of the interval. It must be of the form a * var + b.
1256          is_present: A literal that indicates if the interval is active or not. A
1257            inactive interval is simply ignored by all constraints.
1258          name: The name of the interval variable.
1259
1260        Returns:
1261          An `IntervalVar` object.
1262        """
1263        return self._new_interval_var(
1264            name,
1265            start,
1266            size,
1267            end,
1268            [is_present],
1269        )

Creates an optional interval var from start, size, end, and is_present.

An optional interval variable is a constraint, that is itself used in other constraints like NoOverlap. This constraint is protected by a presence literal that indicates if it is active or not.

Internally, it ensures that is_present implies start + size == end.

Arguments:
  • start: The start of the interval. It must be of the form a * var + b.
  • size: The size of the interval. It must be of the form a * var + b.
  • end: The end of the interval. It must be of the form a * var + b.
  • is_present: A literal that indicates if the interval is active or not. A inactive interval is simply ignored by all constraints.
  • name: The name of the interval variable.
Returns:

An IntervalVar object.

def new_optional_interval_var_series( self, name: str, index: pandas.core.indexes.base.Index, starts: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | pandas.core.series.Series, sizes: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | pandas.core.series.Series, ends: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | pandas.core.series.Series, are_present: ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool | pandas.core.series.Series) -> pandas.core.series.Series:
1271    def new_optional_interval_var_series(
1272        self,
1273        name: str,
1274        index: pd.Index,
1275        starts: Union[LinearExprT, pd.Series],
1276        sizes: Union[LinearExprT, pd.Series],
1277        ends: Union[LinearExprT, pd.Series],
1278        are_present: Union[LiteralT, pd.Series],
1279    ) -> pd.Series:
1280        """Creates a series of interval variables with the given name.
1281
1282        Args:
1283          name (str): Required. The name of the variable set.
1284          index (pd.Index): Required. The index to use for the variable set.
1285          starts (Union[LinearExprT, pd.Series]): The start of each interval in the
1286            set. If a `pd.Series` is passed in, it will be based on the
1287            corresponding values of the pd.Series.
1288          sizes (Union[LinearExprT, pd.Series]): The size of each interval in the
1289            set. If a `pd.Series` is passed in, it will be based on the
1290            corresponding values of the pd.Series.
1291          ends (Union[LinearExprT, pd.Series]): The ends of each interval in the
1292            set. If a `pd.Series` is passed in, it will be based on the
1293            corresponding values of the pd.Series.
1294          are_present (Union[LiteralT, pd.Series]): The performed literal of each
1295            interval in the set. If a `pd.Series` is passed in, it will be based on
1296            the corresponding values of the pd.Series.
1297
1298        Returns:
1299          pd.Series: The interval variable set indexed by its corresponding
1300          dimensions.
1301
1302        Raises:
1303          TypeError: if the `index` is invalid (e.g. a `DataFrame`).
1304          ValueError: if the `name` is not a valid identifier or already exists.
1305          ValueError: if the all the indexes do not match.
1306        """
1307        if not isinstance(index, pd.Index):
1308            raise TypeError("Non-index object is used as index")
1309        if not name.isidentifier():
1310            raise ValueError(f"name={name!r} is not a valid identifier")
1311
1312        starts = _convert_to_series_and_validate_index(starts, index)
1313        sizes = _convert_to_series_and_validate_index(sizes, index)
1314        ends = _convert_to_series_and_validate_index(ends, index)
1315        are_present = _convert_to_series_and_validate_index(are_present, index)
1316
1317        interval_array = []
1318        for i in index:
1319            interval_array.append(
1320                self.new_optional_interval_var(
1321                    start=starts[i],
1322                    size=sizes[i],
1323                    end=ends[i],
1324                    is_present=are_present[i],
1325                    name=f"{name}[{i}]",
1326                )
1327            )
1328        return pd.Series(index=index, data=interval_array)

Creates a series of interval variables with the given name.

Arguments:
  • name (str): Required. The name of the variable set.
  • index (pd.Index): Required. The index to use for the variable set.
  • starts (Union[LinearExprT, pd.Series]): The start of each interval in the set. If a pd.Series is passed in, it will be based on the corresponding values of the pd.Series.
  • sizes (Union[LinearExprT, pd.Series]): The size of each interval in the set. If a pd.Series is passed in, it will be based on the corresponding values of the pd.Series.
  • ends (Union[LinearExprT, pd.Series]): The ends of each interval in the set. If a pd.Series is passed in, it will be based on the corresponding values of the pd.Series.
  • are_present (Union[LiteralT, pd.Series]): The performed literal of each interval in the set. If a pd.Series is passed in, it will be based on the corresponding values of the pd.Series.
Returns:

pd.Series: The interval variable set indexed by its corresponding dimensions.

Raises:
  • TypeError: if the index is invalid (e.g. a DataFrame).
  • ValueError: if the name is not a valid identifier or already exists.
  • ValueError: if the all the indexes do not match.
def new_optional_fixed_size_interval_var( self, start: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, size: int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64, is_present: ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool, name: str) -> IntervalVar:
1330    def new_optional_fixed_size_interval_var(
1331        self,
1332        start: LinearExprT,
1333        size: IntegralT,
1334        is_present: LiteralT,
1335        name: str,
1336    ) -> IntervalVar:
1337        """Creates an interval variable from start, and a fixed size.
1338
1339        An interval variable is a constraint, that is itself used in other
1340        constraints like NoOverlap.
1341
1342        Args:
1343          start: The start of the interval. It must be of the form a * var + b.
1344          size: The size of the interval. It must be an integer value.
1345          is_present: A literal that indicates if the interval is active or not. A
1346            inactive interval is simply ignored by all constraints.
1347          name: The name of the interval variable.
1348
1349        Returns:
1350          An `IntervalVar` object.
1351        """
1352        return self._new_interval_var(
1353            name,
1354            start,
1355            size,
1356            start + size,
1357            [is_present],
1358        )

Creates an interval variable from start, and a fixed size.

An interval variable is a constraint, that is itself used in other constraints like NoOverlap.

Arguments:
  • start: The start of the interval. It must be of the form a * var + b.
  • size: The size of the interval. It must be an integer value.
  • is_present: A literal that indicates if the interval is active or not. A inactive interval is simply ignored by all constraints.
  • name: The name of the interval variable.
Returns:

An IntervalVar object.

def new_optional_fixed_size_interval_var_series( self, name: str, index: pandas.core.indexes.base.Index, starts: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | pandas.core.series.Series, sizes: int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | pandas.core.series.Series, are_present: ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool | pandas.core.series.Series) -> pandas.core.series.Series:
1360    def new_optional_fixed_size_interval_var_series(
1361        self,
1362        name: str,
1363        index: pd.Index,
1364        starts: Union[LinearExprT, pd.Series],
1365        sizes: Union[IntegralT, pd.Series],
1366        are_present: Union[LiteralT, pd.Series],
1367    ) -> pd.Series:
1368        """Creates a series of interval variables with the given name.
1369
1370        Args:
1371          name (str): Required. The name of the variable set.
1372          index (pd.Index): Required. The index to use for the variable set.
1373          starts (Union[LinearExprT, pd.Series]): The start of each interval in the
1374            set. If a `pd.Series` is passed in, it will be based on the
1375            corresponding values of the pd.Series.
1376          sizes (Union[IntegralT, pd.Series]): The fixed size of each interval in
1377            the set. If a `pd.Series` is passed in, it will be based on the
1378            corresponding values of the pd.Series.
1379          are_present (Union[LiteralT, pd.Series]): The performed literal of each
1380            interval in the set. If a `pd.Series` is passed in, it will be based on
1381            the corresponding values of the pd.Series.
1382
1383        Returns:
1384          pd.Series: The interval variable set indexed by its corresponding
1385          dimensions.
1386
1387        Raises:
1388          TypeError: if the `index` is invalid (e.g. a `DataFrame`).
1389          ValueError: if the `name` is not a valid identifier or already exists.
1390          ValueError: if the all the indexes do not match.
1391        """
1392        if not isinstance(index, pd.Index):
1393            raise TypeError("Non-index object is used as index")
1394        if not name.isidentifier():
1395            raise ValueError(f"name={name!r} is not a valid identifier")
1396
1397        starts = _convert_to_series_and_validate_index(starts, index)
1398        sizes = _convert_to_series_and_validate_index(sizes, index)
1399        are_present = _convert_to_series_and_validate_index(are_present, index)
1400        interval_array = []
1401        for i in index:
1402            interval_array.append(
1403                self.new_optional_fixed_size_interval_var(
1404                    start=starts[i],
1405                    size=sizes[i],
1406                    is_present=are_present[i],
1407                    name=f"{name}[{i}]",
1408                )
1409            )
1410        return pd.Series(index=index, data=interval_array)

Creates a series of interval variables with the given name.

Arguments:
  • name (str): Required. The name of the variable set.
  • index (pd.Index): Required. The index to use for the variable set.
  • starts (Union[LinearExprT, pd.Series]): The start of each interval in the set. If a pd.Series is passed in, it will be based on the corresponding values of the pd.Series.
  • sizes (Union[IntegralT, pd.Series]): The fixed size of each interval in the set. If a pd.Series is passed in, it will be based on the corresponding values of the pd.Series.
  • are_present (Union[LiteralT, pd.Series]): The performed literal of each interval in the set. If a pd.Series is passed in, it will be based on the corresponding values of the pd.Series.
Returns:

pd.Series: The interval variable set indexed by its corresponding dimensions.

Raises:
  • TypeError: if the index is invalid (e.g. a DataFrame).
  • ValueError: if the name is not a valid identifier or already exists.
  • ValueError: if the all the indexes do not match.
def add_no_overlap( self, intervals: Iterable[IntervalVar]) -> Constraint:
1412    def add_no_overlap(self, intervals: Iterable[IntervalVar]) -> Constraint:
1413        """Adds NoOverlap(interval_vars).
1414
1415        A NoOverlap constraint ensures that all present intervals do not overlap
1416        in time.
1417
1418        Args:
1419          intervals: The list of interval variables to constrain.
1420
1421        Returns:
1422          An instance of the `Constraint` class.
1423        """
1424        return self._add_no_overlap(intervals)

Adds NoOverlap(interval_vars).

A NoOverlap constraint ensures that all present intervals do not overlap in time.

Arguments:
  • intervals: The list of interval variables to constrain.
Returns:

An instance of the Constraint class.

def add_no_overlap_2d( self, x_intervals: Iterable[IntervalVar], y_intervals: Iterable[IntervalVar]) -> Constraint:
1426    def add_no_overlap_2d(
1427        self,
1428        x_intervals: Iterable[IntervalVar],
1429        y_intervals: Iterable[IntervalVar],
1430    ) -> Constraint:
1431        """Adds NoOverlap2D(x_intervals, y_intervals).
1432
1433        A NoOverlap2D constraint ensures that all present rectangles do not overlap
1434        on a plane. Each rectangle is aligned with the X and Y axis, and is defined
1435        by two intervals which represent its projection onto the X and Y axis.
1436
1437        Furthermore, one box is optional if at least one of the x or y interval is
1438        optional.
1439
1440        Args:
1441          x_intervals: The X coordinates of the rectangles.
1442          y_intervals: The Y coordinates of the rectangles.
1443
1444        Returns:
1445          An instance of the `Constraint` class.
1446        """
1447        return self._add_no_overlap_2d(x_intervals, y_intervals)

Adds NoOverlap2D(x_intervals, y_intervals).

A NoOverlap2D constraint ensures that all present rectangles do not overlap on a plane. Each rectangle is aligned with the X and Y axis, and is defined by two intervals which represent its projection onto the X and Y axis.

Furthermore, one box is optional if at least one of the x or y interval is optional.

Arguments:
  • x_intervals: The X coordinates of the rectangles.
  • y_intervals: The Y coordinates of the rectangles.
Returns:

An instance of the Constraint class.

def add_cumulative( self, intervals: Iterable[IntervalVar], demands: Iterable[LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64], capacity: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64) -> Constraint:
1449    def add_cumulative(
1450        self,
1451        intervals: Iterable[IntervalVar],
1452        demands: Iterable[LinearExprT],
1453        capacity: LinearExprT,
1454    ) -> Constraint:
1455        """Adds Cumulative(intervals, demands, capacity).
1456
1457        This constraint enforces that:
1458
1459            for all t:
1460              sum(demands[i]
1461                if (start(intervals[i]) <= t < end(intervals[i])) and
1462                (intervals[i] is present)) <= capacity
1463
1464        Args:
1465          intervals: The list of intervals.
1466          demands: The list of demands for each interval. Each demand must be >= 0.
1467            Each demand can be a 1-var affine expression (a * x + b).
1468          capacity: The maximum capacity of the cumulative constraint. It can be a
1469            1-var affine expression (a * x + b).
1470
1471        Returns:
1472          An instance of the `Constraint` class.
1473        """
1474        return self._add_cumulative(intervals, demands, capacity)

Adds Cumulative(intervals, demands, capacity).

This constraint enforces that:

for all t: sum(demands[i] if (start(intervals[i]) <= t < end(intervals[i])) and (intervals[i] is present)) <= capacity

Arguments:
  • intervals: The list of intervals.
  • demands: The list of demands for each interval. Each demand must be >= 0. Each demand can be a 1-var affine expression (a * x + b).
  • capacity: The maximum capacity of the cumulative constraint. It can be a 1-var affine expression (a * x + b).
Returns:

An instance of the Constraint class.

def clone(self) -> CpModel:
1477    def clone(self) -> "CpModel":
1478        """Reset the model, and creates a new one from a CpModelProto instance."""
1479        clone = CpModel()
1480        clone.proto.copy_from(self.proto)
1481        clone.rebuild_constant_map()
1482        return clone

Reset the model, and creates a new one from a CpModelProto instance.

def get_bool_var_from_proto_index(self, index: int) -> IntVar:
1490    def get_bool_var_from_proto_index(self, index: int) -> IntVar:
1491        """Returns an already created Boolean variable from its index."""
1492        if index < 0 or index >= len(self.model_proto.variables):
1493            raise ValueError(
1494                f"get_bool_var_from_proto_index: out of bound index {index}"
1495            )
1496        result = IntVar(self.model_proto, index)
1497        if not result.is_boolean:
1498            raise TypeError(
1499                f"get_bool_var_from_proto_index: index {index} does not reference a"
1500                " boolean variable"
1501            )
1502        return result

Returns an already created Boolean variable from its index.

def get_int_var_from_proto_index(self, index: int) -> IntVar:
1504    def get_int_var_from_proto_index(self, index: int) -> IntVar:
1505        """Returns an already created integer variable from its index."""
1506        if index < 0 or index >= len(self.model_proto.variables):
1507            raise ValueError(
1508                f"get_int_var_from_proto_index: out of bound index {index}"
1509            )
1510        return IntVar(self.model_proto, index)

Returns an already created integer variable from its index.

def get_interval_var_from_proto_index(self, index: int) -> IntervalVar:
1512    def get_interval_var_from_proto_index(self, index: int) -> IntervalVar:
1513        """Returns an already created interval variable from its index."""
1514        if index < 0 or index >= len(self.model_proto.constraints):
1515            raise ValueError(
1516                f"get_interval_var_from_proto_index: out of bound index {index}"
1517            )
1518        ct = self.model_proto.constraints[index]
1519        if not ct.has_interval():
1520            raise ValueError(
1521                f"get_interval_var_from_proto_index: index {index} does not"
1522                " reference an" + " interval variable"
1523            )
1524
1525        return IntervalVar(self.model_proto, index)

Returns an already created interval variable from its index.

proto: CpModelProto
1530    @property
1531    def proto(self) -> cmh.CpModelProto:
1532        """Returns the underlying CpModelProto."""
1533        return self.model_proto

Returns the underlying CpModelProto.

def negated(self, index: int) -> int:
1535    def negated(self, index: int) -> int:
1536        return -index - 1
def minimize( self, obj: LinearExpr | int | float | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | numpy.float64):
1572    def minimize(self, obj: ObjLinearExprT):
1573        """Sets the objective of the model to minimize(obj)."""
1574        self._set_objective(obj, maximize=False)

Sets the objective of the model to minimize(obj).

def maximize( self, obj: LinearExpr | int | float | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | numpy.float64):
1576    def maximize(self, obj: ObjLinearExprT):
1577        """Sets the objective of the model to maximize(obj)."""
1578        self._set_objective(obj, maximize=True)

Sets the objective of the model to maximize(obj).

def has_objective(self) -> bool:
1580    def has_objective(self) -> bool:
1581        return (
1582            self.model_proto.has_objective()
1583            or self.model_proto.has_floating_point_objective()
1584        )
def clear_objective(self):
1586    def clear_objective(self):
1587        self.model_proto.clear_objective()
1588        self.model_proto.clear_floating_point_objective()
def add_decision_strategy( self, variables: Iterable[IntVar], var_strategy: ortools.sat.python.cp_model_helper.DecisionStrategyProto.VariableSelectionStrategy, domain_strategy: ortools.sat.python.cp_model_helper.DecisionStrategyProto.DomainReductionStrategy) -> None:
1590    def add_decision_strategy(
1591        self,
1592        variables: Iterable[IntVar],
1593        var_strategy: cmh.DecisionStrategyProto.VariableSelectionStrategy,
1594        domain_strategy: cmh.DecisionStrategyProto.DomainReductionStrategy,
1595    ) -> None:
1596        """Adds a search strategy to the model.
1597
1598        Args:
1599          variables: a list of variables this strategy will assign.
1600          var_strategy: heuristic to choose the next variable to assign.
1601          domain_strategy: heuristic to reduce the domain of the selected variable.
1602            Currently, this is advanced code: the union of all strategies added to
1603            the model must be complete, i.e. instantiates all variables. Otherwise,
1604            solve() will fail.
1605        """
1606
1607        strategy: cmh.DecisionStrategyProto = self.model_proto.search_strategy.add()
1608        for v in variables:
1609            expr = strategy.exprs.add()
1610            if v.index >= 0:
1611                expr.vars.append(v.index)
1612                expr.coeffs.append(1)
1613            else:
1614                expr.vars.append(self.negated(v.index))
1615                expr.coeffs.append(-1)
1616                expr.offset = 1
1617
1618        strategy.variable_selection_strategy = var_strategy
1619        strategy.domain_reduction_strategy = domain_strategy

Adds a search strategy to the model.

Arguments:
  • variables: a list of variables this strategy will assign.
  • var_strategy: heuristic to choose the next variable to assign.
  • domain_strategy: heuristic to reduce the domain of the selected variable. Currently, this is advanced code: the union of all strategies added to the model must be complete, i.e. instantiates all variables. Otherwise, solve() will fail.
def model_stats(self) -> str:
1621    def model_stats(self) -> str:
1622        """Returns a string containing some model statistics."""
1623        return cmh.CpSatHelper.model_stats(self.model_proto)

Returns a string containing some model statistics.

def validate(self) -> str:
1625    def validate(self) -> str:
1626        """Returns a string indicating that the model is invalid."""
1627        return cmh.CpSatHelper.validate_model(self.model_proto)

Returns a string indicating that the model is invalid.

def export_to_file(self, file: str) -> bool:
1629    def export_to_file(self, file: str) -> bool:
1630        """Write the model as a protocol buffer to 'file'.
1631
1632        Args:
1633          file: file to write the model to. If the filename ends with 'txt', the
1634            model will be written as a text file, otherwise, the binary format will
1635            be used.
1636
1637        Returns:
1638          True if the model was correctly written.
1639        """
1640        return cmh.CpSatHelper.write_model_to_file(self.model_proto, file)

Write the model as a protocol buffer to 'file'.

Arguments:
  • file: file to write the model to. If the filename ends with 'txt', the model will be written as a text file, otherwise, the binary format will be used.
Returns:

True if the model was correctly written.

def remove_all_names(self) -> None:
1642    def remove_all_names(self) -> None:
1643        """Removes all names from the model."""
1644        self.model_proto.clear_name()
1645        for v in self.model_proto.variables:
1646            v.clear_name()
1647        for c in self.model_proto.constraints:
1648            c.clear_name()

Removes all names from the model.

def add_hint(self, var, value) -> None:
1656    def add_hint(self, var, value) -> None:
1657        """Adds 'var == value' as a hint to the solver."""
1658        if var.index >= 0:
1659            self.model_proto.solution_hint.vars.append(var.index)
1660            self.model_proto.solution_hint.values.append(int(value))
1661        else:
1662            self.model_proto.solution_hint.vars.append(self.negated(var.index))
1663            self.model_proto.solution_hint.values.append(int(not value))

Adds 'var == value' as a hint to the solver.

def clear_hints(self):
1665    def clear_hints(self):
1666        """Removes any solution hint from the model."""
1667        self.model_proto.clear_solution_hint()

Removes any solution hint from the model.

def add_assumption( self, lit: ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool) -> None:
1669    def add_assumption(self, lit: LiteralT) -> None:
1670        """Adds the literal to the model as assumptions."""
1671        self.model_proto.assumptions.append(self.get_or_make_boolean_index(lit))

Adds the literal to the model as assumptions.

def add_assumptions( self, literals: Iterable[ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool]) -> None:
1673    def add_assumptions(self, literals: Iterable[LiteralT]) -> None:
1674        """Adds the literals to the model as assumptions."""
1675        for lit in literals:
1676            self.add_assumption(lit)

Adds the literals to the model as assumptions.

def clear_assumptions(self) -> None:
1678    def clear_assumptions(self) -> None:
1679        """Removes all assumptions from the model."""
1680        self.model_proto.assumptions.clear()

Removes all assumptions from the model.

def Name(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def SetName(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def Proto(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

class CpSolver:
1734class CpSolver:
1735    """Main solver class.
1736
1737    The purpose of this class is to search for a solution to the model provided
1738    to the solve() method.
1739
1740    Once solve() is called, this class allows inspecting the solution found
1741    with the value() and boolean_value() methods, as well as general statistics
1742    about the solve procedure.
1743    """
1744
1745    def __init__(self) -> None:
1746        self.__response: Optional[cmh.CpSolverResponse] = None
1747        self.parameters: cmh.SatParameters = cmh.SatParameters()
1748        self.log_callback: Optional[Callable[[str], None]] = None
1749        self.best_bound_callback: Optional[Callable[[float], None]] = None
1750        self.__solve_wrapper: Optional[cmh.SolveWrapper] = None
1751        self.__lock: threading.Lock = threading.Lock()
1752
1753    def solve(
1754        self,
1755        model: CpModel,
1756        solution_callback: Optional["CpSolverSolutionCallback"] = None,
1757    ) -> cmh.CpSolverStatus:
1758        """Solves a problem and passes each solution to the callback if not null."""
1759        with self.__lock:
1760            self.__solve_wrapper = cmh.SolveWrapper()
1761
1762        self.__solve_wrapper.set_parameters(self.parameters)
1763        if solution_callback is not None:
1764            self.__solve_wrapper.add_solution_callback(solution_callback)
1765
1766        if self.log_callback is not None:
1767            self.__solve_wrapper.add_log_callback(self.log_callback)
1768
1769        if self.best_bound_callback is not None:
1770            self.__solve_wrapper.add_best_bound_callback(self.best_bound_callback)
1771
1772        self.__response = self.__solve_wrapper.solve(model.proto)
1773
1774        if solution_callback is not None:
1775            self.__solve_wrapper.clear_solution_callback(solution_callback)
1776
1777        with self.__lock:
1778            self.__solve_wrapper = None
1779
1780        return self.__response.status
1781
1782    def stop_search(self) -> None:
1783        """Stops the current search asynchronously."""
1784        with self.__lock:
1785            if self.__solve_wrapper:
1786                self.__solve_wrapper.stop_search()
1787
1788    def value(self, expression: LinearExprT) -> int:
1789        """Returns the value of a linear expression after solve."""
1790        return cmh.ResponseHelper.value(self._checked_response, expression)
1791
1792    def values(self, variables: _IndexOrSeries) -> pd.Series:
1793        """Returns the values of the input variables.
1794
1795        If `variables` is a `pd.Index`, then the output will be indexed by the
1796        variables. If `variables` is a `pd.Series` indexed by the underlying
1797        dimensions, then the output will be indexed by the same underlying
1798        dimensions.
1799
1800        Args:
1801          variables (Union[pd.Index, pd.Series]): The set of variables from which to
1802            get the values.
1803
1804        Returns:
1805          pd.Series: The values of all variables in the set.
1806
1807        Raises:
1808          RuntimeError: if solve() has not been called.
1809        """
1810        response: cmh.CpSolverResponse = self._checked_response
1811        return pd.Series(
1812            data=[cmh.ResponseHelper.value(response, var) for var in variables],
1813            index=_get_index(variables),
1814        )
1815
1816    def float_value(self, expression: LinearExprT) -> float:
1817        """Returns the value of a linear expression after solve."""
1818        return cmh.ResponseHelper.float_value(self._checked_response, expression)
1819
1820    def float_values(self, expressions: _IndexOrSeries) -> pd.Series:
1821        """Returns the float values of the input linear expressions.
1822
1823        If `expressions` is a `pd.Index`, then the output will be indexed by the
1824        variables. If `variables` is a `pd.Series` indexed by the underlying
1825        dimensions, then the output will be indexed by the same underlying
1826        dimensions.
1827
1828        Args:
1829          expressions (Union[pd.Index, pd.Series]): The set of expressions from
1830            which to get the values.
1831
1832        Returns:
1833          pd.Series: The values of all variables in the set.
1834
1835        Raises:
1836          RuntimeError: if solve() has not been called.
1837        """
1838        response: cmh.CpSolverResponse = self._checked_response
1839        return pd.Series(
1840            data=[
1841                cmh.ResponseHelper.float_value(response, expr) for expr in expressions
1842            ],
1843            index=_get_index(expressions),
1844        )
1845
1846    def boolean_value(self, literal: LiteralT) -> bool:
1847        """Returns the boolean value of a literal after solve."""
1848        return cmh.ResponseHelper.boolean_value(self._checked_response, literal)
1849
1850    def boolean_values(self, variables: _IndexOrSeries) -> pd.Series:
1851        """Returns the values of the input variables.
1852
1853        If `variables` is a `pd.Index`, then the output will be indexed by the
1854        variables. If `variables` is a `pd.Series` indexed by the underlying
1855        dimensions, then the output will be indexed by the same underlying
1856        dimensions.
1857
1858        Args:
1859          variables (Union[pd.Index, pd.Series]): The set of variables from which to
1860            get the values.
1861
1862        Returns:
1863          pd.Series: The values of all variables in the set.
1864
1865        Raises:
1866          RuntimeError: if solve() has not been called.
1867        """
1868        response: cmh.CpSolverResponse = self._checked_response
1869        return pd.Series(
1870            data=[
1871                cmh.ResponseHelper.boolean_value(response, literal)
1872                for literal in variables
1873            ],
1874            index=_get_index(variables),
1875        )
1876
1877    @property
1878    def objective_value(self) -> float:
1879        """Returns the value of the objective after solve."""
1880        return self._checked_response.objective_value
1881
1882    @property
1883    def best_objective_bound(self) -> float:
1884        """Returns the best lower (upper) bound found when min(max)imizing."""
1885        return self._checked_response.best_objective_bound
1886
1887    @property
1888    def num_booleans(self) -> int:
1889        """Returns the number of boolean variables managed by the SAT solver."""
1890        return self._checked_response.num_booleans
1891
1892    @property
1893    def num_conflicts(self) -> int:
1894        """Returns the number of conflicts since the creation of the solver."""
1895        return self._checked_response.num_conflicts
1896
1897    @property
1898    def num_branches(self) -> int:
1899        """Returns the number of search branches explored by the solver."""
1900        return self._checked_response.num_branches
1901
1902    @property
1903    def num_binary_propagations(self) -> int:
1904        """Returns the number of Boolean propagations done by the solver."""
1905        return self._checked_response.num_binary_propagations
1906
1907    @property
1908    def num_integer_propagations(self) -> int:
1909        """Returns the number of integer propagations done by the solver."""
1910        return self._checked_response.num_integer_propagations
1911
1912    @property
1913    def deterministic_time(self) -> float:
1914        """Returns the deterministic time in seconds since the creation of the solver."""
1915        return self._checked_response.deterministic_time
1916
1917    @property
1918    def wall_time(self) -> float:
1919        """Returns the wall time in seconds since the creation of the solver."""
1920        return self._checked_response.wall_time
1921
1922    @property
1923    def user_time(self) -> float:
1924        """Returns the user time in seconds since the creation of the solver."""
1925        return self._checked_response.user_time
1926
1927    @property
1928    def solve_log(self) -> str:
1929        """Returns the solve log.
1930
1931        To enable this, the parameter log_to_response must be set to True.
1932        """
1933        return self._checked_response.solve_log
1934
1935    @property
1936    def solve_info(self) -> str:
1937        """Returns the information about the solve."""
1938        return self._checked_response.solve_info
1939
1940    @property
1941    def response_proto(self) -> cmh.CpSolverResponse:
1942        """Returns the response object."""
1943        return self._checked_response
1944
1945    def response_stats(self) -> str:
1946        """Returns some statistics on the solution found as a string."""
1947        return cmh.CpSatHelper.solver_response_stats(self._checked_response)
1948
1949    def sufficient_assumptions_for_infeasibility(self) -> Sequence[int]:
1950        """Returns the indices of the infeasible assumptions."""
1951        return cmh.ResponseHelper.sufficient_assumptions_for_infeasibility(
1952            self._checked_response
1953        )
1954
1955    def status_name(self, status: Optional[Any] = None) -> str:
1956        """Returns the name of the status returned by solve()."""
1957        if status is None:
1958            status = self._checked_response.status()
1959        return status.name
1960
1961    def solution_info(self) -> str:
1962        """Returns some information on the solve process.
1963
1964        Returns some information on how the solution was found, or the reason
1965        why the model or the parameters are invalid.
1966
1967        Raises:
1968          RuntimeError: if solve() has not been called.
1969        """
1970        return self._checked_response.solution_info
1971
1972    @property
1973    def _checked_response(self) -> cmh.CpSolverResponse:
1974        """Checks solve() has been called, and returns a response wrapper."""
1975        if self.__response is None:
1976            raise RuntimeError("solve() has not been called.")
1977        return self.__response
1978
1979    # Compatibility with pre PEP8
1980    # pylint: disable=invalid-name
1981
1982    @deprecated("Use best_objective_bound property instead.")
1983    def BestObjectiveBound(self) -> float:
1984        return self.best_objective_bound
1985
1986    @deprecated("Use boolean_value() method instead.")
1987    def BooleanValue(self, lit: LiteralT) -> bool:
1988        return self.boolean_value(lit)
1989
1990    @deprecated("Use boolean_values() method instead.")
1991    def BooleanValues(self, variables: _IndexOrSeries) -> pd.Series:
1992        return self.boolean_values(variables)
1993
1994    @deprecated("Use num_booleans property instead.")
1995    def NumBooleans(self) -> int:
1996        return self.num_booleans
1997
1998    @deprecated("Use num_conflicts property instead.")
1999    def NumConflicts(self) -> int:
2000        return self.num_conflicts
2001
2002    @deprecated("Use num_branches property instead.")
2003    def NumBranches(self) -> int:
2004        return self.num_branches
2005
2006    @deprecated("Use objective_value property instead.")
2007    def ObjectiveValue(self) -> float:
2008        return self.objective_value
2009
2010    @deprecated("Use response_proto property instead.")
2011    def ResponseProto(self) -> cmh.CpSolverResponse:
2012        return self.response_proto
2013
2014    @deprecated("Use response_stats() method instead.")
2015    def ResponseStats(self) -> str:
2016        return self.response_stats()
2017
2018    @deprecated("Use solve() method instead.")
2019    def Solve(
2020        self, model: CpModel, callback: "CpSolverSolutionCallback" = None
2021    ) -> cmh.CpSolverStatus:
2022        return self.solve(model, callback)
2023
2024    @deprecated("Use solution_info() method instead.")
2025    def SolutionInfo(self) -> str:
2026        return self.solution_info()
2027
2028    @deprecated("Use status_name() method instead.")
2029    def StatusName(self, status: Optional[Any] = None) -> str:
2030        return self.status_name(status)
2031
2032    @deprecated("Use stop_search() method instead.")
2033    def StopSearch(self) -> None:
2034        self.stop_search()
2035
2036    @deprecated("Use sufficient_assumptions_for_infeasibility() method instead.")
2037    def SufficientAssumptionsForInfeasibility(self) -> Sequence[int]:
2038        return self.sufficient_assumptions_for_infeasibility()
2039
2040    @deprecated("Use user_time property instead.")
2041    def UserTime(self) -> float:
2042        return self.user_time
2043
2044    @deprecated("Use value() method instead.")
2045    def Value(self, expression: LinearExprT) -> int:
2046        return self.value(expression)
2047
2048    @deprecated("Use values() method instead.")
2049    def Values(self, expressions: _IndexOrSeries) -> pd.Series:
2050        return self.values(expressions)
2051
2052    @deprecated("Use wall_time property instead.")
2053    def WallTime(self) -> float:
2054        return self.wall_time
2055
2056    @deprecated("Use solve() with enumerate_all_solutions = True.")
2057    def SearchForAllSolutions(
2058        self, model: CpModel, callback: "CpSolverSolutionCallback"
2059    ) -> cmh.CpSolverStatus:
2060        """Search for all solutions of a satisfiability problem.
2061
2062        This method searches for all feasible solutions of a given model.
2063        Then it feeds the solution to the callback.
2064
2065        Note that the model cannot contain an objective.
2066
2067        Args:
2068          model: The model to solve.
2069          callback: The callback that will be called at each solution.
2070
2071        Returns:
2072          The status of the solve:
2073
2074          * *FEASIBLE* if some solutions have been found
2075          * *INFEASIBLE* if the solver has proved there are no solution
2076          * *OPTIMAL* if all solutions have been found
2077        """
2078        if model.has_objective():
2079            raise TypeError(
2080                "Search for all solutions is only defined on satisfiability problems"
2081            )
2082        # Store old parameter.
2083        enumerate_all = self.parameters.enumerate_all_solutions
2084        self.parameters.enumerate_all_solutions = True
2085
2086        status: cmh.CpSolverStatus = self.solve(model, callback)
2087
2088        # Restore parameter.
2089        self.parameters.enumerate_all_solutions = enumerate_all
2090        return status

Main solver class.

The purpose of this class is to search for a solution to the model provided to the solve() method.

Once solve() is called, this class allows inspecting the solution found with the value() and boolean_value() methods, as well as general statistics about the solve procedure.

parameters: SatParameters
log_callback: Callable[[str], None] | None
best_bound_callback: Callable[[float], None] | None
def solve( self, model: CpModel, solution_callback: CpSolverSolutionCallback | None = None) -> CpSolverStatus:
1753    def solve(
1754        self,
1755        model: CpModel,
1756        solution_callback: Optional["CpSolverSolutionCallback"] = None,
1757    ) -> cmh.CpSolverStatus:
1758        """Solves a problem and passes each solution to the callback if not null."""
1759        with self.__lock:
1760            self.__solve_wrapper = cmh.SolveWrapper()
1761
1762        self.__solve_wrapper.set_parameters(self.parameters)
1763        if solution_callback is not None:
1764            self.__solve_wrapper.add_solution_callback(solution_callback)
1765
1766        if self.log_callback is not None:
1767            self.__solve_wrapper.add_log_callback(self.log_callback)
1768
1769        if self.best_bound_callback is not None:
1770            self.__solve_wrapper.add_best_bound_callback(self.best_bound_callback)
1771
1772        self.__response = self.__solve_wrapper.solve(model.proto)
1773
1774        if solution_callback is not None:
1775            self.__solve_wrapper.clear_solution_callback(solution_callback)
1776
1777        with self.__lock:
1778            self.__solve_wrapper = None
1779
1780        return self.__response.status

Solves a problem and passes each solution to the callback if not null.

def value( self, expression: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64) -> int:
1788    def value(self, expression: LinearExprT) -> int:
1789        """Returns the value of a linear expression after solve."""
1790        return cmh.ResponseHelper.value(self._checked_response, expression)

Returns the value of a linear expression after solve.

def values( self, variables: pandas.core.indexes.base.Index | pandas.core.series.Series) -> pandas.core.series.Series:
1792    def values(self, variables: _IndexOrSeries) -> pd.Series:
1793        """Returns the values of the input variables.
1794
1795        If `variables` is a `pd.Index`, then the output will be indexed by the
1796        variables. If `variables` is a `pd.Series` indexed by the underlying
1797        dimensions, then the output will be indexed by the same underlying
1798        dimensions.
1799
1800        Args:
1801          variables (Union[pd.Index, pd.Series]): The set of variables from which to
1802            get the values.
1803
1804        Returns:
1805          pd.Series: The values of all variables in the set.
1806
1807        Raises:
1808          RuntimeError: if solve() has not been called.
1809        """
1810        response: cmh.CpSolverResponse = self._checked_response
1811        return pd.Series(
1812            data=[cmh.ResponseHelper.value(response, var) for var in variables],
1813            index=_get_index(variables),
1814        )

Returns the values of the input variables.

If variables is a pd.Index, then the output will be indexed by the variables. If variables is a pd.Series indexed by the underlying dimensions, then the output will be indexed by the same underlying dimensions.

Arguments:
  • variables (Union[pd.Index, pd.Series]): The set of variables from which to get the values.
Returns:

pd.Series: The values of all variables in the set.

Raises:
  • RuntimeError: if solve() has not been called.
def float_value( self, expression: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64) -> float:
1816    def float_value(self, expression: LinearExprT) -> float:
1817        """Returns the value of a linear expression after solve."""
1818        return cmh.ResponseHelper.float_value(self._checked_response, expression)

Returns the value of a linear expression after solve.

def float_values( self, expressions: pandas.core.indexes.base.Index | pandas.core.series.Series) -> pandas.core.series.Series:
1820    def float_values(self, expressions: _IndexOrSeries) -> pd.Series:
1821        """Returns the float values of the input linear expressions.
1822
1823        If `expressions` is a `pd.Index`, then the output will be indexed by the
1824        variables. If `variables` is a `pd.Series` indexed by the underlying
1825        dimensions, then the output will be indexed by the same underlying
1826        dimensions.
1827
1828        Args:
1829          expressions (Union[pd.Index, pd.Series]): The set of expressions from
1830            which to get the values.
1831
1832        Returns:
1833          pd.Series: The values of all variables in the set.
1834
1835        Raises:
1836          RuntimeError: if solve() has not been called.
1837        """
1838        response: cmh.CpSolverResponse = self._checked_response
1839        return pd.Series(
1840            data=[
1841                cmh.ResponseHelper.float_value(response, expr) for expr in expressions
1842            ],
1843            index=_get_index(expressions),
1844        )

Returns the float values of the input linear expressions.

If expressions is a pd.Index, then the output will be indexed by the variables. If variables is a pd.Series indexed by the underlying dimensions, then the output will be indexed by the same underlying dimensions.

Arguments:
  • expressions (Union[pd.Index, pd.Series]): The set of expressions from which to get the values.
Returns:

pd.Series: The values of all variables in the set.

Raises:
  • RuntimeError: if solve() has not been called.
def boolean_value( self, literal: ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool) -> bool:
1846    def boolean_value(self, literal: LiteralT) -> bool:
1847        """Returns the boolean value of a literal after solve."""
1848        return cmh.ResponseHelper.boolean_value(self._checked_response, literal)

Returns the boolean value of a literal after solve.

def boolean_values( self, variables: pandas.core.indexes.base.Index | pandas.core.series.Series) -> pandas.core.series.Series:
1850    def boolean_values(self, variables: _IndexOrSeries) -> pd.Series:
1851        """Returns the values of the input variables.
1852
1853        If `variables` is a `pd.Index`, then the output will be indexed by the
1854        variables. If `variables` is a `pd.Series` indexed by the underlying
1855        dimensions, then the output will be indexed by the same underlying
1856        dimensions.
1857
1858        Args:
1859          variables (Union[pd.Index, pd.Series]): The set of variables from which to
1860            get the values.
1861
1862        Returns:
1863          pd.Series: The values of all variables in the set.
1864
1865        Raises:
1866          RuntimeError: if solve() has not been called.
1867        """
1868        response: cmh.CpSolverResponse = self._checked_response
1869        return pd.Series(
1870            data=[
1871                cmh.ResponseHelper.boolean_value(response, literal)
1872                for literal in variables
1873            ],
1874            index=_get_index(variables),
1875        )

Returns the values of the input variables.

If variables is a pd.Index, then the output will be indexed by the variables. If variables is a pd.Series indexed by the underlying dimensions, then the output will be indexed by the same underlying dimensions.

Arguments:
  • variables (Union[pd.Index, pd.Series]): The set of variables from which to get the values.
Returns:

pd.Series: The values of all variables in the set.

Raises:
  • RuntimeError: if solve() has not been called.
objective_value: float
1877    @property
1878    def objective_value(self) -> float:
1879        """Returns the value of the objective after solve."""
1880        return self._checked_response.objective_value

Returns the value of the objective after solve.

best_objective_bound: float
1882    @property
1883    def best_objective_bound(self) -> float:
1884        """Returns the best lower (upper) bound found when min(max)imizing."""
1885        return self._checked_response.best_objective_bound

Returns the best lower (upper) bound found when min(max)imizing.

num_booleans: int
1887    @property
1888    def num_booleans(self) -> int:
1889        """Returns the number of boolean variables managed by the SAT solver."""
1890        return self._checked_response.num_booleans

Returns the number of boolean variables managed by the SAT solver.

num_conflicts: int
1892    @property
1893    def num_conflicts(self) -> int:
1894        """Returns the number of conflicts since the creation of the solver."""
1895        return self._checked_response.num_conflicts

Returns the number of conflicts since the creation of the solver.

num_branches: int
1897    @property
1898    def num_branches(self) -> int:
1899        """Returns the number of search branches explored by the solver."""
1900        return self._checked_response.num_branches

Returns the number of search branches explored by the solver.

num_binary_propagations: int
1902    @property
1903    def num_binary_propagations(self) -> int:
1904        """Returns the number of Boolean propagations done by the solver."""
1905        return self._checked_response.num_binary_propagations

Returns the number of Boolean propagations done by the solver.

num_integer_propagations: int
1907    @property
1908    def num_integer_propagations(self) -> int:
1909        """Returns the number of integer propagations done by the solver."""
1910        return self._checked_response.num_integer_propagations

Returns the number of integer propagations done by the solver.

deterministic_time: float
1912    @property
1913    def deterministic_time(self) -> float:
1914        """Returns the deterministic time in seconds since the creation of the solver."""
1915        return self._checked_response.deterministic_time

Returns the deterministic time in seconds since the creation of the solver.

wall_time: float
1917    @property
1918    def wall_time(self) -> float:
1919        """Returns the wall time in seconds since the creation of the solver."""
1920        return self._checked_response.wall_time

Returns the wall time in seconds since the creation of the solver.

user_time: float
1922    @property
1923    def user_time(self) -> float:
1924        """Returns the user time in seconds since the creation of the solver."""
1925        return self._checked_response.user_time

Returns the user time in seconds since the creation of the solver.

solve_log: str
1927    @property
1928    def solve_log(self) -> str:
1929        """Returns the solve log.
1930
1931        To enable this, the parameter log_to_response must be set to True.
1932        """
1933        return self._checked_response.solve_log

Returns the solve log.

To enable this, the parameter log_to_response must be set to True.

solve_info: str
1935    @property
1936    def solve_info(self) -> str:
1937        """Returns the information about the solve."""
1938        return self._checked_response.solve_info

Returns the information about the solve.

response_proto: CpSolverResponse
1940    @property
1941    def response_proto(self) -> cmh.CpSolverResponse:
1942        """Returns the response object."""
1943        return self._checked_response

Returns the response object.

def response_stats(self) -> str:
1945    def response_stats(self) -> str:
1946        """Returns some statistics on the solution found as a string."""
1947        return cmh.CpSatHelper.solver_response_stats(self._checked_response)

Returns some statistics on the solution found as a string.

def sufficient_assumptions_for_infeasibility(self) -> Sequence[int]:
1949    def sufficient_assumptions_for_infeasibility(self) -> Sequence[int]:
1950        """Returns the indices of the infeasible assumptions."""
1951        return cmh.ResponseHelper.sufficient_assumptions_for_infeasibility(
1952            self._checked_response
1953        )

Returns the indices of the infeasible assumptions.

def status_name(self, status: Any | None = None) -> str:
1955    def status_name(self, status: Optional[Any] = None) -> str:
1956        """Returns the name of the status returned by solve()."""
1957        if status is None:
1958            status = self._checked_response.status()
1959        return status.name

Returns the name of the status returned by solve().

def solution_info(self) -> str:
1961    def solution_info(self) -> str:
1962        """Returns some information on the solve process.
1963
1964        Returns some information on how the solution was found, or the reason
1965        why the model or the parameters are invalid.
1966
1967        Raises:
1968          RuntimeError: if solve() has not been called.
1969        """
1970        return self._checked_response.solution_info

Returns some information on the solve process.

Returns some information on how the solution was found, or the reason why the model or the parameters are invalid.

Raises:
  • RuntimeError: if solve() has not been called.
def BestObjectiveBound(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def BooleanValue(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def BooleanValues(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def NumBooleans(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def NumConflicts(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def NumBranches(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def ObjectiveValue(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def ResponseProto(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def ResponseStats(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def Solve(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def SolutionInfo(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def StatusName(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def StopSearch(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def SufficientAssumptionsForInfeasibility(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def UserTime(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def Value(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def Values(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def WallTime(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

def SearchForAllSolutions(*args, **kwargs):
196        def deprecated_func(*args, **kwargs):
197            if enable_warnings:
198                warnings.warn(
199                    f"{func.__name__} is a deprecated function. {message}",
200                    category=DeprecationWarning,
201                    stacklevel=2,
202                )
203                warnings.simplefilter("default", DeprecationWarning)
204            return func(*args, **kwargs)

The type of the None singleton.

class CpSolverSolutionCallback(ortools.sat.python.cp_model_helper.SolutionCallback):
2096class CpSolverSolutionCallback(cmh.SolutionCallback):
2097    """Solution callback.
2098
2099    This class implements a callback that will be called at each new solution
2100    found during search.
2101
2102    The method on_solution_callback() will be called by the solver, and must be
2103    implemented. The current solution can be queried using the boolean_value()
2104    and value() methods.
2105
2106    These methods returns the same information as their counterpart in the
2107    `CpSolver` class.
2108    """
2109
2110    def __init__(self) -> None:
2111        cmh.SolutionCallback.__init__(self)
2112
2113    # pylint: disable=invalid-name
2114    def OnSolutionCallback(self) -> None:
2115        """Proxy for the same method in snake case."""
2116        self.on_solution_callback()
2117
2118    # pylint: enable=invalid-name
2119
2120    def boolean_value(self, lit: LiteralT) -> bool:
2121        """Returns the boolean value of a boolean literal.
2122
2123        Args:
2124            lit: A boolean variable or its negation.
2125
2126        Returns:
2127            The Boolean value of the literal in the solution.
2128
2129        Raises:
2130            RuntimeError: if `lit` is not a boolean variable or its negation.
2131        """
2132        if not self.has_response():
2133            raise RuntimeError("solve() has not been called.")
2134        return self.BooleanValue(lit)
2135
2136    def value(self, expression: LinearExprT) -> int:
2137        """Evaluates an linear expression in the current solution.
2138
2139        Args:
2140            expression: a linear expression of the model.
2141
2142        Returns:
2143            An integer value equal to the evaluation of the linear expression
2144            against the current solution.
2145
2146        Raises:
2147            RuntimeError: if 'expression' is not a LinearExpr.
2148        """
2149        if not self.has_response():
2150            raise RuntimeError("solve() has not been called.")
2151        return self.Value(expression)
2152
2153    def float_value(self, expression: LinearExprT) -> float:
2154        """Evaluates an linear expression in the current solution.
2155
2156        Args:
2157            expression: a linear expression of the model.
2158
2159        Returns:
2160            An integer value equal to the evaluation of the linear expression
2161            against the current solution.
2162
2163        Raises:
2164            RuntimeError: if 'expression' is not a LinearExpr.
2165        """
2166        if not self.has_response():
2167            raise RuntimeError("solve() has not been called.")
2168        return self.FloatValue(expression)
2169
2170    def has_response(self) -> bool:
2171        return self.HasResponse()
2172
2173    def stop_search(self) -> None:
2174        """Stops the current search asynchronously."""
2175        if not self.has_response():
2176            raise RuntimeError("solve() has not been called.")
2177        self.StopSearch()
2178
2179    @property
2180    def objective_value(self) -> float:
2181        """Returns the value of the objective after solve."""
2182        if not self.has_response():
2183            raise RuntimeError("solve() has not been called.")
2184        return self.ObjectiveValue()
2185
2186    @property
2187    def best_objective_bound(self) -> float:
2188        """Returns the best lower (upper) bound found when min(max)imizing."""
2189        if not self.has_response():
2190            raise RuntimeError("solve() has not been called.")
2191        return self.BestObjectiveBound()
2192
2193    @property
2194    def num_booleans(self) -> int:
2195        """Returns the number of boolean variables managed by the SAT solver."""
2196        if not self.has_response():
2197            raise RuntimeError("solve() has not been called.")
2198        return self.NumBooleans()
2199
2200    @property
2201    def num_conflicts(self) -> int:
2202        """Returns the number of conflicts since the creation of the solver."""
2203        if not self.has_response():
2204            raise RuntimeError("solve() has not been called.")
2205        return self.NumConflicts()
2206
2207    @property
2208    def num_branches(self) -> int:
2209        """Returns the number of search branches explored by the solver."""
2210        if not self.has_response():
2211            raise RuntimeError("solve() has not been called.")
2212        return self.NumBranches()
2213
2214    @property
2215    def num_integer_propagations(self) -> int:
2216        """Returns the number of integer propagations done by the solver."""
2217        if not self.has_response():
2218            raise RuntimeError("solve() has not been called.")
2219        return self.NumIntegerPropagations()
2220
2221    @property
2222    def num_binary_propagations(self) -> int:
2223        """Returns the number of Boolean propagations done by the solver."""
2224        if not self.has_response():
2225            raise RuntimeError("solve() has not been called.")
2226        return self.NumBinaryPropagations()
2227
2228    @property
2229    def deterministic_time(self) -> float:
2230        """Returns the determistic time in seconds since the creation of the solver."""
2231        if not self.has_response():
2232            raise RuntimeError("solve() has not been called.")
2233        return self.DeterministicTime()
2234
2235    @property
2236    def wall_time(self) -> float:
2237        """Returns the wall time in seconds since the creation of the solver."""
2238        if not self.has_response():
2239            raise RuntimeError("solve() has not been called.")
2240        return self.WallTime()
2241
2242    @property
2243    def user_time(self) -> float:
2244        """Returns the user time in seconds since the creation of the solver."""
2245        if not self.has_response():
2246            raise RuntimeError("solve() has not been called.")
2247        return self.UserTime()
2248
2249    @property
2250    def response_proto(self) -> cmh.CpSolverResponse:
2251        """Returns the response object."""
2252        if not self.has_response():
2253            raise RuntimeError("solve() has not been called.")
2254        return self.Response()

Solution callback.

This class implements a callback that will be called at each new solution found during search.

The method on_solution_callback() will be called by the solver, and must be implemented. The current solution can be queried using the boolean_value() and value() methods.

These methods returns the same information as their counterpart in the CpSolver class.

CpSolverSolutionCallback()
2110    def __init__(self) -> None:
2111        cmh.SolutionCallback.__init__(self)
def OnSolutionCallback(self) -> None:
2114    def OnSolutionCallback(self) -> None:
2115        """Proxy for the same method in snake case."""
2116        self.on_solution_callback()

Proxy for the same method in snake case.

def boolean_value( self, lit: ortools.sat.python.cp_model_helper.Literal | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64 | bool) -> bool:
2120    def boolean_value(self, lit: LiteralT) -> bool:
2121        """Returns the boolean value of a boolean literal.
2122
2123        Args:
2124            lit: A boolean variable or its negation.
2125
2126        Returns:
2127            The Boolean value of the literal in the solution.
2128
2129        Raises:
2130            RuntimeError: if `lit` is not a boolean variable or its negation.
2131        """
2132        if not self.has_response():
2133            raise RuntimeError("solve() has not been called.")
2134        return self.BooleanValue(lit)

Returns the boolean value of a boolean literal.

Arguments:
  • lit: A boolean variable or its negation.
Returns:

The Boolean value of the literal in the solution.

Raises:
  • RuntimeError: if lit is not a boolean variable or its negation.
def value( self, expression: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64) -> int:
2136    def value(self, expression: LinearExprT) -> int:
2137        """Evaluates an linear expression in the current solution.
2138
2139        Args:
2140            expression: a linear expression of the model.
2141
2142        Returns:
2143            An integer value equal to the evaluation of the linear expression
2144            against the current solution.
2145
2146        Raises:
2147            RuntimeError: if 'expression' is not a LinearExpr.
2148        """
2149        if not self.has_response():
2150            raise RuntimeError("solve() has not been called.")
2151        return self.Value(expression)

Evaluates an linear expression in the current solution.

Arguments:
  • expression: a linear expression of the model.
Returns:

An integer value equal to the evaluation of the linear expression against the current solution.

Raises:
  • RuntimeError: if 'expression' is not a LinearExpr.
def float_value( self, expression: LinearExpr | IntVar | int | numpy.int8 | numpy.uint8 | numpy.int32 | numpy.uint32 | numpy.int64 | numpy.uint64) -> float:
2153    def float_value(self, expression: LinearExprT) -> float:
2154        """Evaluates an linear expression in the current solution.
2155
2156        Args:
2157            expression: a linear expression of the model.
2158
2159        Returns:
2160            An integer value equal to the evaluation of the linear expression
2161            against the current solution.
2162
2163        Raises:
2164            RuntimeError: if 'expression' is not a LinearExpr.
2165        """
2166        if not self.has_response():
2167            raise RuntimeError("solve() has not been called.")
2168        return self.FloatValue(expression)

Evaluates an linear expression in the current solution.

Arguments:
  • expression: a linear expression of the model.
Returns:

An integer value equal to the evaluation of the linear expression against the current solution.

Raises:
  • RuntimeError: if 'expression' is not a LinearExpr.
def has_response(self) -> bool:
2170    def has_response(self) -> bool:
2171        return self.HasResponse()
objective_value: float
2179    @property
2180    def objective_value(self) -> float:
2181        """Returns the value of the objective after solve."""
2182        if not self.has_response():
2183            raise RuntimeError("solve() has not been called.")
2184        return self.ObjectiveValue()

Returns the value of the objective after solve.

best_objective_bound: float
2186    @property
2187    def best_objective_bound(self) -> float:
2188        """Returns the best lower (upper) bound found when min(max)imizing."""
2189        if not self.has_response():
2190            raise RuntimeError("solve() has not been called.")
2191        return self.BestObjectiveBound()

Returns the best lower (upper) bound found when min(max)imizing.

num_booleans: int
2193    @property
2194    def num_booleans(self) -> int:
2195        """Returns the number of boolean variables managed by the SAT solver."""
2196        if not self.has_response():
2197            raise RuntimeError("solve() has not been called.")
2198        return self.NumBooleans()

Returns the number of boolean variables managed by the SAT solver.

num_conflicts: int
2200    @property
2201    def num_conflicts(self) -> int:
2202        """Returns the number of conflicts since the creation of the solver."""
2203        if not self.has_response():
2204            raise RuntimeError("solve() has not been called.")
2205        return self.NumConflicts()

Returns the number of conflicts since the creation of the solver.

num_branches: int
2207    @property
2208    def num_branches(self) -> int:
2209        """Returns the number of search branches explored by the solver."""
2210        if not self.has_response():
2211            raise RuntimeError("solve() has not been called.")
2212        return self.NumBranches()

Returns the number of search branches explored by the solver.

num_integer_propagations: int
2214    @property
2215    def num_integer_propagations(self) -> int:
2216        """Returns the number of integer propagations done by the solver."""
2217        if not self.has_response():
2218            raise RuntimeError("solve() has not been called.")
2219        return self.NumIntegerPropagations()

Returns the number of integer propagations done by the solver.

num_binary_propagations: int
2221    @property
2222    def num_binary_propagations(self) -> int:
2223        """Returns the number of Boolean propagations done by the solver."""
2224        if not self.has_response():
2225            raise RuntimeError("solve() has not been called.")
2226        return self.NumBinaryPropagations()

Returns the number of Boolean propagations done by the solver.

deterministic_time: float
2228    @property
2229    def deterministic_time(self) -> float:
2230        """Returns the determistic time in seconds since the creation of the solver."""
2231        if not self.has_response():
2232            raise RuntimeError("solve() has not been called.")
2233        return self.DeterministicTime()

Returns the determistic time in seconds since the creation of the solver.

wall_time: float
2235    @property
2236    def wall_time(self) -> float:
2237        """Returns the wall time in seconds since the creation of the solver."""
2238        if not self.has_response():
2239            raise RuntimeError("solve() has not been called.")
2240        return self.WallTime()

Returns the wall time in seconds since the creation of the solver.

user_time: float
2242    @property
2243    def user_time(self) -> float:
2244        """Returns the user time in seconds since the creation of the solver."""
2245        if not self.has_response():
2246            raise RuntimeError("solve() has not been called.")
2247        return self.UserTime()

Returns the user time in seconds since the creation of the solver.

response_proto: CpSolverResponse
2249    @property
2250    def response_proto(self) -> cmh.CpSolverResponse:
2251        """Returns the response object."""
2252        if not self.has_response():
2253            raise RuntimeError("solve() has not been called.")
2254        return self.Response()

Returns the response object.

class ObjectiveSolutionPrinter(CpSolverSolutionCallback):
2257class ObjectiveSolutionPrinter(CpSolverSolutionCallback):
2258    """Display the objective value and time of intermediate solutions."""
2259
2260    def __init__(self) -> None:
2261        CpSolverSolutionCallback.__init__(self)
2262        self.__solution_count = 0
2263        self.__start_time = time.time()
2264
2265    def on_solution_callback(self) -> None:
2266        """Called on each new solution."""
2267        current_time = time.time()
2268        obj = self.objective_value
2269        print(
2270            f"Solution {self.__solution_count}, time ="
2271            f" {current_time - self.__start_time:0.2f} s, objective = {obj}",
2272            flush=True,
2273        )
2274        self.__solution_count += 1
2275
2276    def solution_count(self) -> int:
2277        """Returns the number of solutions found."""
2278        return self.__solution_count

Display the objective value and time of intermediate solutions.

ObjectiveSolutionPrinter()
2260    def __init__(self) -> None:
2261        CpSolverSolutionCallback.__init__(self)
2262        self.__solution_count = 0
2263        self.__start_time = time.time()
def on_solution_callback(self) -> None:
2265    def on_solution_callback(self) -> None:
2266        """Called on each new solution."""
2267        current_time = time.time()
2268        obj = self.objective_value
2269        print(
2270            f"Solution {self.__solution_count}, time ="
2271            f" {current_time - self.__start_time:0.2f} s, objective = {obj}",
2272            flush=True,
2273        )
2274        self.__solution_count += 1

Called on each new solution.

def solution_count(self) -> int:
2276    def solution_count(self) -> int:
2277        """Returns the number of solutions found."""
2278        return self.__solution_count

Returns the number of solutions found.

class VarArrayAndObjectiveSolutionPrinter(CpSolverSolutionCallback):
2281class VarArrayAndObjectiveSolutionPrinter(CpSolverSolutionCallback):
2282    """Print intermediate solutions (objective, variable values, time)."""
2283
2284    def __init__(self, variables: Sequence[IntVar]) -> None:
2285        CpSolverSolutionCallback.__init__(self)
2286        self.__variables: Sequence[IntVar] = variables
2287        self.__solution_count: int = 0
2288        self.__start_time: float = time.time()
2289
2290    def on_solution_callback(self) -> None:
2291        """Called on each new solution."""
2292        current_time = time.time()
2293        obj = self.objective_value
2294        print(
2295            f"Solution {self.__solution_count}, time ="
2296            f" {current_time - self.__start_time:0.2f} s, objective = {obj}"
2297        )
2298        for v in self.__variables:
2299            print(f"  {v} = {self.value(v)}", end=" ")
2300        print(flush=True)
2301        self.__solution_count += 1
2302
2303    @property
2304    def solution_count(self) -> int:
2305        """Returns the number of solutions found."""
2306        return self.__solution_count

Print intermediate solutions (objective, variable values, time).

VarArrayAndObjectiveSolutionPrinter(variables: Sequence[IntVar])
2284    def __init__(self, variables: Sequence[IntVar]) -> None:
2285        CpSolverSolutionCallback.__init__(self)
2286        self.__variables: Sequence[IntVar] = variables
2287        self.__solution_count: int = 0
2288        self.__start_time: float = time.time()
def on_solution_callback(self) -> None:
2290    def on_solution_callback(self) -> None:
2291        """Called on each new solution."""
2292        current_time = time.time()
2293        obj = self.objective_value
2294        print(
2295            f"Solution {self.__solution_count}, time ="
2296            f" {current_time - self.__start_time:0.2f} s, objective = {obj}"
2297        )
2298        for v in self.__variables:
2299            print(f"  {v} = {self.value(v)}", end=" ")
2300        print(flush=True)
2301        self.__solution_count += 1

Called on each new solution.

solution_count: int
2303    @property
2304    def solution_count(self) -> int:
2305        """Returns the number of solutions found."""
2306        return self.__solution_count

Returns the number of solutions found.

class VarArraySolutionPrinter(CpSolverSolutionCallback):
2309class VarArraySolutionPrinter(CpSolverSolutionCallback):
2310    """Print intermediate solutions (variable values, time)."""
2311
2312    def __init__(self, variables: Sequence[IntVar]) -> None:
2313        CpSolverSolutionCallback.__init__(self)
2314        self.__variables: Sequence[IntVar] = variables
2315        self.__solution_count: int = 0
2316        self.__start_time: float = time.time()
2317
2318    def on_solution_callback(self) -> None:
2319        """Called on each new solution."""
2320        current_time = time.time()
2321        print(
2322            f"Solution {self.__solution_count}, time ="
2323            f" {current_time - self.__start_time:0.2f} s"
2324        )
2325        for v in self.__variables:
2326            print(f"  {v} = {self.value(v)}", end=" ")
2327        print(flush=True)
2328        self.__solution_count += 1
2329
2330    @property
2331    def solution_count(self) -> int:
2332        """Returns the number of solutions found."""
2333        return self.__solution_count

Print intermediate solutions (variable values, time).

VarArraySolutionPrinter(variables: Sequence[IntVar])
2312    def __init__(self, variables: Sequence[IntVar]) -> None:
2313        CpSolverSolutionCallback.__init__(self)
2314        self.__variables: Sequence[IntVar] = variables
2315        self.__solution_count: int = 0
2316        self.__start_time: float = time.time()
def on_solution_callback(self) -> None:
2318    def on_solution_callback(self) -> None:
2319        """Called on each new solution."""
2320        current_time = time.time()
2321        print(
2322            f"Solution {self.__solution_count}, time ="
2323            f" {current_time - self.__start_time:0.2f} s"
2324        )
2325        for v in self.__variables:
2326            print(f"  {v} = {self.value(v)}", end=" ")
2327        print(flush=True)
2328        self.__solution_count += 1

Called on each new solution.

solution_count: int
2330    @property
2331    def solution_count(self) -> int:
2332        """Returns the number of solutions found."""
2333        return self.__solution_count

Returns the number of solutions found.