Skip to content

XplorCplexCP

XplorCplexCP

XplorCplexCP(model: CpoModel | None = None)

Bases: XplorModel[CpoModel, CpoVariable, CpoExpr]

Xplor wrapper for CPLEX CP (Constraint Programming) solver.

This class provides a Polars-based interface for constraint programming problems like scheduling (RCPSP, job shop) using CPLEX CP Optimizer via docplex.cp.

Unlike the mathematical programming backends (XplorCplex, XplorGurobi), this uses interval variables and CP-specific constraints.

Attributes:

Name Type Description
model CpoModel

The underlying CPLEX CP model

Examples:

>>> import polars as pl
>>> from xplor.cplex_cp import XplorCplexCP
>>> xmodel = XplorCplexCP()
>>> # Create tasks DataFrame
>>> df = pl.DataFrame({
...     "task": ["T1", "T2", "T3"],
...     "duration": [3, 5, 4]
... })
>>> # Add interval variables
>>> df = df.with_columns(xmodel.add_interval_vars("iv", duration="duration"))
>>> # Add precedence constraint: T1 must finish before T2 starts
>>> xmodel.add_constr(
...     xmodel.var.iv.filter(pl.col("task") == "T1")
...         .end_before_start(xmodel.var.iv.filter(pl.col("task") == "T2"))
... )

Initialize the XplorCplexCP wrapper.

Parameters:

Name Type Description Default
model CpoModel | None

Optional pre-existing CPLEX CP model. If None, creates a new model.

None
Source code in src/xplor/cplex_cp/model.py
def __init__(self, model: CpoModel | None = None) -> None:
    """Initialize the XplorCplexCP wrapper.

    Parameters
    ----------
    model : docplex.cp.model.CpoModel | None, default None
        Optional pre-existing CPLEX CP model. If None, creates a new model.

    """
    self.solution: CpoSolveResult | None = None
    super().__init__(CpoModel() if model is None else model)

Attributes

var cached property

var: _ProxyCplexCPVarExpr

Entry point for creating interval variable expressions.

Similar to polars.col(), allows referencing interval variables created via add_interval_vars() in constraint expressions.

Returns:

Type Description
_ProxyCplexCPVarExpr

Proxy object for creating interval variable expressions

Examples:

>>> xmodel.var("task_iv")
>>> xmodel.var.task_iv

Functions

add_vars

add_vars(
    name: str,
    *,
    lb: float | str | Expr = 0.0,
    ub: float | str | Expr | None = None,
    obj: float | str | Expr | None = None,
    indices: Expr | list[str] | None = None,
    vtype: VariableType = "CONTINUOUS",
    priority: int | Expr = 0,
) -> Expr

Define and return a Var expression for optimization variables.

This method generates a Polars expression that, when consumed (e.g., via .with_columns()), creates optimization variables for every row and adds them to the underlying solver model.

Parameters:

Name Type Description Default
name str

The base name for the variables (e.g., "production" or "flow"). This name is used to retrieve variable values after optimization.

required
lb float | str | Expr

Lower bound for created variables. Can be a scalar, a column name (str), or a Polars expression.

0.0
ub float | str | Expr | None

Upper bound for created variables. If None, the solver default is used.

None
obj float | str | Expr | None

Objective function coefficient for created variables. Can be a scalar, a column name, or a Polars expression.

None
indices Expr | list[str] | None

Keys (column names) that uniquely identify each variable instance. Used to format the internal variable names (e.g., 'x[1,2]').

None
vtype VariableType

The type of the variable (CONTINUOUS, INTEGER, or BINARY).

'CONTINUOUS'
priority int | Expr

Multi-objective optimization priority. Higher priority numbers are optimized FIRST (priority 2 before priority 1 before priority 0). All objectives with the same priority are combined into a single weighted sum. Currently only supported by the Gurobi backend.

0

Returns:

Type Description
Expr

A Polars expression (Var) that, when executed, adds variables to the model and returns them as an Object Series in the DataFrame.

Examples:

Assuming xmodel is an instance of a concrete class (XplorGurobi).

>>> # 1. Basic variable creation using columns for bounds:
>>> data = pl.DataFrame({"max_limit": [10.0, 5.0]})
>>> df = data.with_columns(
...     xmodel.add_vars("x", lb=0.0, ub=pl.col("max_limit"), obj=1.0)
... )
>>> # df["x"] now contains gurobipy.Var or mathopt.Variable objects.
>>> # s2. Creating integer variables indexed by two columns:
>>> data = pl.DataFrame({"time": [1, 1, 2, 2], "resource": ["A", "B", "A", "B"]})
>>> df = data.with_columns(
...     xmodel.add_vars(
...         "sched",
...         indices=["time", "resource"],
...         vtype=VarType.INTEGER,
...     )
... )
>>> # Variable names will look like 'sched[1,A]', 'sched[1,B]', etc.
Source code in src/xplor/model.py
def add_vars(
    self,
    name: str,
    *,
    lb: float | str | pl.Expr = 0.0,
    ub: float | str | pl.Expr | None = None,
    obj: float | str | pl.Expr | None = None,
    indices: pl.Expr | list[str] | None = None,
    vtype: VariableType = "CONTINUOUS",
    priority: int | pl.Expr = 0,
) -> pl.Expr:
    """Define and return a Var expression for optimization variables.

    This method generates a Polars expression that, when consumed (e.g., via
    `.with_columns()`), creates optimization variables for every row and adds
    them to the underlying solver model.

    Parameters
    ----------
    name : str
        The base name for the variables (e.g., "production" or "flow").
        This name is used to retrieve variable values after optimization.
    lb : float | str | pl.Expr, default 0.0
        Lower bound for created variables. Can be a scalar, a column name (str),
        or a Polars expression.
    ub : float | str | pl.Expr | None, default None
        Upper bound for created variables. If None, the solver default is used.
    obj: float | str | pl.Expr, default 0.0
        Objective function coefficient for created variables. Can be a scalar,
        a column name, or a Polars expression.
    indices: pl.Expr | None, default pl.row_index()
        Keys (column names) that uniquely identify each variable instance.
        Used to format the internal variable names (e.g., 'x[1,2]').
    vtype: VariableType
        The type of the variable (CONTINUOUS, INTEGER, or BINARY).
    priority: int | pl.Expr, default 0
        Multi-objective optimization priority. Higher priority numbers are optimized
        FIRST (priority 2 before priority 1 before priority 0). All objectives with
        the same priority are combined into a single weighted sum. Currently only
        supported by the Gurobi backend.

    Returns
    -------
    pl.Expr
        A Polars expression (`Var`) that, when executed, adds variables to the model
        and returns them as an `Object` Series in the DataFrame.

    Examples
    --------
    Assuming `xmodel` is an instance of a concrete class (`XplorGurobi`).

    >>> # 1. Basic variable creation using columns for bounds:
    >>> data = pl.DataFrame({"max_limit": [10.0, 5.0]})
    >>> df = data.with_columns(
    ...     xmodel.add_vars("x", lb=0.0, ub=pl.col("max_limit"), obj=1.0)
    ... )
    >>> # df["x"] now contains gurobipy.Var or mathopt.Variable objects.

    >>> # s2. Creating integer variables indexed by two columns:
    >>> data = pl.DataFrame({"time": [1, 1, 2, 2], "resource": ["A", "B", "A", "B"]})
    >>> df = data.with_columns(
    ...     xmodel.add_vars(
    ...         "sched",
    ...         indices=["time", "resource"],
    ...         vtype=VarType.INTEGER,
    ...     )
    ... )
    >>> # Variable names will look like 'sched[1,A]', 'sched[1,B]', etc.

    """
    indices = pl.concat_str(pl.row_index() if indices is None else indices, separator=",")
    return pl.map_batches(
        [
            parse_into_expr(lb).alias("lb"),
            parse_into_expr(ub).alias("ub"),
            parse_into_expr(obj).alias("obj"),
            parse_into_expr(priority).cast(pl.Int64).alias("priority"),
            pl.format(f"{name}[{{}}]", indices).alias("name"),
        ],
        lambda s: self._add_vars_wrapper(series_to_df(s), name=name, vtype=vtype),
        return_dtype=pl.Object,
    ).alias(name)

add_constrs

add_constrs(
    df: DataFrame,
    *constr_exprs: ConstrExpr,
    indices: Expr | list[str] | None = None,
    **named_constr_exprs: ConstrExpr,
) -> DataFrame

Define and return a Constr expression for model constraints.

This method accepts a symbolic relational expression (e.g., x <= 5) and generates a Polars expression that, when consumed (e.g., via .select()), adds the constraints to the underlying solver model.

The constraint is added row-wise if the input expression is a Series of expressions, or as a single constraint if the expression is aggregated (e.g., using .sum()).

Parameters:

Name Type Description Default
df DataFrame

The polars DataFrame used to create the constraints

required
constr_exprs ConstrExpr

The constraints expression (e.g., a relational expression like xplor.var("x").sum() <= 10).

()
indices Expr | list[str] | None

Keys (column names) that uniquely identify each constraint instance. Used to format the internal variable names (e.g., 'constr[1,2]').

None
named_constr_exprs ConstrExpr

Other constraints expression

{}

Returns:

Type Description
Expr

A Polars expression (Constr) that, when executed, adds constraints to the model and returns them as an Object Series in the DataFrame.

.. warning::

All constraints provided within a single call to add_constrs should have the same granularity (i.e., correspond to the same set of indices). If constraints with different granularities are provided, Polars' broadcasting mechanism might lead to constraints being added multiple times to the optimization model.

Examples:

Assuming df has been created and contains the variable Series df["x"].

>>> df.pipe(
...     xmodel.add_constrs,
        max_per_item = xplor.var("x") <= pl.col("capacity"),
        min_per_item = xplor.var("x") >= pl.col("min_threshold"),
        indices=["product"]
... )
Source code in src/xplor/model.py
def add_constrs(
    self,
    df: pl.DataFrame,
    *constr_exprs: ConstrExpr,
    indices: pl.Expr | list[str] | None = None,
    **named_constr_exprs: ConstrExpr,
) -> pl.DataFrame:
    r"""Define and return a Constr expression for model constraints.

    This method accepts a symbolic relational expression (e.g., `x <= 5`)
    and generates a Polars expression that, when consumed (e.g., via `.select()`),
    adds the constraints to the underlying solver model.

    The constraint is added row-wise if the input expression is a Series of
    expressions, or as a single constraint if the expression is aggregated
    (e.g., using `.sum()`).

    Parameters
    ----------
    df: pl.DataFrame
        The polars DataFrame used to create the constraints
    constr_exprs : ConstrExpr
        The constraints expression (e.g., a relational expression like
        `xplor.var("x").sum() <= 10`).
    indices: pl.Expr | None, default None
        Keys (column names) that uniquely identify each constraint instance.
        Used to format the internal variable names (e.g., 'constr[1,2]').
    named_constr_exprs : ConstrExpr
        Other constraints expression

    Returns
    -------
    pl.Expr
        A Polars expression (`Constr`) that, when executed, adds constraints
        to the model and returns them as an `Object` Series in the DataFrame.

    .. warning::
        All constraints provided within a single call to `add_constrs` should
        have the same granularity (i.e., correspond to the same set of indices).
        If constraints with different granularities are provided, Polars'
        broadcasting mechanism might lead to constraints being added multiple
        times to the optimization model.

    Examples
    --------
    Assuming `df` has been created and contains the variable Series `df["x"]`.

    >>> df.pipe(
    ...     xmodel.add_constrs,
            max_per_item = xplor.var("x") <= pl.col("capacity"),
            min_per_item = xplor.var("x") >= pl.col("min_threshold"),
            indices=["product"]
    ... )


    """
    if isinstance(indices, list):
        indices = pl.concat_str(indices, separator=",")

    for expr in constr_exprs:
        name = str(expr)
        assert name not in named_constr_exprs, f"Duplicated name for constraint {name}"
        named_constr_exprs[name] = expr

    # Process multi-output and single-output constraints separately
    exprs: list[pl.Expr] = []
    constrs_repr_d: dict[str, ExpressionRepr] = {}

    for name, expr in named_constr_exprs.items():
        if expr.meta.has_multiple_outputs():
            self._process_multi_output_constraint(df, expr, indices)
        else:
            constrs_repr_d[name], exprs = expr.parse(exprs)
    if constrs_repr_d:
        self._process_single_output_constraints(df, constrs_repr_d, exprs, indices)

    return df

minimize

minimize(
    df: DataFrame,
    /,
    priority: int = 0,
    **named_obj_exprs: VarExpr,
) -> DataFrame

Add minimization objectives to the model.

This method accepts named objective expressions and adds them to the model at the specified priority level. Multiple objectives at the same priority are combined into a single weighted sum.

Parameters:

Name Type Description Default
df DataFrame

The polars DataFrame used to evaluate the objective expressions.

required
priority int

Multi-objective optimization priority. Higher priority numbers are optimized FIRST (priority 2 before priority 1 before priority 0). All objectives with the same priority are combined into a single weighted sum. Currently only supported by the Gurobi backend.

0
named_obj_exprs Expr

Named objective expressions to minimize (e.g., sum_x = xplor.var("x").sum()).

{}

Returns:

Type Description
DataFrame

The input DataFrame (unchanged), allowing for method chaining.

Examples:

Assuming df has been created and contains the variable Series df["x"].

>>> df.pipe(
...     xmodel.minimize,
...     total_cost = (xplor.var("x") * pl.col("cost")).sum(),
...     priority=1
... )
>>> # Multi-objective optimization
>>> df.pipe(
...     xmodel.minimize,
...     sum_x = xplor.var("x").sum(),
...     sum_y = xplor.var("y").sum(),
...     priority=2
... )
Source code in src/xplor/model.py
def minimize(
    self,
    df: pl.DataFrame,
    /,
    priority: int = 0,
    **named_obj_exprs: VarExpr,
) -> pl.DataFrame:
    """Add minimization objectives to the model.

    This method accepts named objective expressions and adds them to the model
    at the specified priority level. Multiple objectives at the same priority
    are combined into a single weighted sum.

    Parameters
    ----------
    df : pl.DataFrame
        The polars DataFrame used to evaluate the objective expressions.
    priority : int, default 0
        Multi-objective optimization priority. Higher priority numbers are optimized
        FIRST (priority 2 before priority 1 before priority 0). All objectives with
        the same priority are combined into a single weighted sum. Currently only
        supported by the Gurobi backend.
    named_obj_exprs : pl.Expr
        Named objective expressions to minimize (e.g., `sum_x = xplor.var("x").sum()`).

    Returns
    -------
    pl.DataFrame
        The input DataFrame (unchanged), allowing for method chaining.

    Examples
    --------
    Assuming `df` has been created and contains the variable Series `df["x"]`.

    >>> df.pipe(
    ...     xmodel.minimize,
    ...     total_cost = (xplor.var("x") * pl.col("cost")).sum(),
    ...     priority=1
    ... )

    >>> # Multi-objective optimization
    >>> df.pipe(
    ...     xmodel.minimize,
    ...     sum_x = xplor.var("x").sum(),
    ...     sum_y = xplor.var("y").sum(),
    ...     priority=2
    ... )

    """
    self._add_objectives(df, named_obj_exprs, priority, sense="minimize")
    return df

maximize

maximize(
    df: DataFrame,
    /,
    priority: int = 0,
    **named_obj_exprs: VarExpr,
) -> DataFrame

Add maximization objectives to the model.

This method accepts named objective expressions and adds them to the model at the specified priority level. Multiple objectives at the same priority are combined into a single weighted sum. Internally, maximization is converted to minimization by negating the objective coefficients.

Parameters:

Name Type Description Default
df DataFrame

The polars DataFrame used to evaluate the objective expressions.

required
priority int

Multi-objective optimization priority. Higher priority numbers are optimized FIRST (priority 2 before priority 1 before priority 0). All objectives with the same priority are combined into a single weighted sum. Currently only supported by the Gurobi backend.

0
named_obj_exprs Expr

Named objective expressions to maximize (e.g., sum_x = xplor.var("x").sum()).

{}

Returns:

Type Description
DataFrame

The input DataFrame (unchanged), allowing for method chaining.

Examples:

Assuming df has been created and contains the variable Series df["x"].

>>> df.pipe(
...     xmodel.maximize,
...     total_revenue = (xplor.var("x") * pl.col("revenue")).sum(),
...     priority=1
... )
>>> # Multi-objective optimization
>>> df.pipe(
...     xmodel.maximize,
...     sum_x = xplor.var("x").sum(),
...     sum_y = xplor.var("y").sum(),
...     priority=2
... )
Source code in src/xplor/model.py
def maximize(
    self,
    df: pl.DataFrame,
    /,
    priority: int = 0,
    **named_obj_exprs: VarExpr,
) -> pl.DataFrame:
    """Add maximization objectives to the model.

    This method accepts named objective expressions and adds them to the model
    at the specified priority level. Multiple objectives at the same priority
    are combined into a single weighted sum. Internally, maximization is converted
    to minimization by negating the objective coefficients.

    Parameters
    ----------
    df : pl.DataFrame
        The polars DataFrame used to evaluate the objective expressions.
    priority : int, default 0
        Multi-objective optimization priority. Higher priority numbers are optimized
        FIRST (priority 2 before priority 1 before priority 0). All objectives with
        the same priority are combined into a single weighted sum. Currently only
        supported by the Gurobi backend.
    named_obj_exprs : pl.Expr
        Named objective expressions to maximize (e.g., `sum_x = xplor.var("x").sum()`).

    Returns
    -------
    pl.DataFrame
        The input DataFrame (unchanged), allowing for method chaining.

    Examples
    --------
    Assuming `df` has been created and contains the variable Series `df["x"]`.

    >>> df.pipe(
    ...     xmodel.maximize,
    ...     total_revenue = (xplor.var("x") * pl.col("revenue")).sum(),
    ...     priority=1
    ... )

    >>> # Multi-objective optimization
    >>> df.pipe(
    ...     xmodel.maximize,
    ...     sum_x = xplor.var("x").sum(),
    ...     sum_y = xplor.var("y").sum(),
    ...     priority=2
    ... )

    """
    self._add_objectives(df, named_obj_exprs, priority, sense="maximize")
    return df

read_values

read_values(name: Expr) -> Expr

Read the value of an optimization variable from the solution.

For interval variables, returns a struct with start, end, length, and present fields. For regular variables, returns the variable value.

Parameters:

Name Type Description Default
name Expr

Expression to evaluate.

required

Returns:

Type Description
Expr

Values of the variable expression. For interval variables, returns a struct with fields: start (Int64), end (Int64), length (Int64), present (Boolean).

Examples:

>>> xmodel: XplorModel
>>> # For regular variables
>>> df_with_solution = df.with_columns(xmodel.read_values(pl.col("x")))
>>> # For interval variables - returns struct with start/end/length/present
>>> df = df.with_columns(xmodel.read_values(pl.col("task_iv")))
>>> df = df.with_columns(
...     start=pl.col("task_iv").struct.field("start"),
...     end=pl.col("task_iv").struct.field("end"),
... )
Source code in src/xplor/cplex_cp/model.py
def read_values(self, name: pl.Expr) -> pl.Expr:
    """Read the value of an optimization variable from the solution.

    For interval variables, returns a struct with start, end, length, and present fields.
    For regular variables, returns the variable value.

    Parameters
    ----------
    name : pl.Expr
        Expression to evaluate.

    Returns
    -------
    pl.Expr
        Values of the variable expression. For interval variables, returns a struct
        with fields: start (Int64), end (Int64), length (Int64), present (Boolean).

    Examples
    --------
    >>> xmodel: XplorModel
    >>> # For regular variables
    >>> df_with_solution = df.with_columns(xmodel.read_values(pl.col("x")))  # doctest: +SKIP
    >>> # For interval variables - returns struct with start/end/length/present
    >>> df = df.with_columns(xmodel.read_values(pl.col("task_iv")))  # doctest: +SKIP
    >>> df = df.with_columns(  # doctest: +SKIP
    ...     start=pl.col("task_iv").struct.field("start"),
    ...     end=pl.col("task_iv").struct.field("end"),
    ... )

    """

    def _extract_interval(v: Any) -> dict[str, Any]:
        """Extract interval variable solution as struct."""
        if v is None or self.solution is None:
            return {"start": None, "end": None, "length": None, "present": None}
        sol = self.solution.get_var_solution(v)
        return {
            "start": sol.start,
            "end": sol.end,
            "length": sol.length,
            "present": sol.is_present(),
        }

    def _extract_regular(v: Any) -> float | None:
        """Extract regular variable solution value."""
        if v is None:
            return None
        if hasattr(v, "solution_value"):
            return v.solution_value
        return float(v)

    def _batch_extract(series: pl.Series) -> pl.Series:
        """Determine variable type and extract appropriately."""
        if len(series) == 0:
            return series

        # Check if this is an interval variable column (internal marker)
        if self.var_types.get(series.name) == "_INTERVAL_CP":
            # Return struct with start/end/length/present
            data = [_extract_interval(v) for v in series]
            return pl.Series(
                series.name,
                data,
                dtype=pl.Struct(
                    {
                        "start": pl.Int64,
                        "end": pl.Int64,
                        "length": pl.Int64,
                        "present": pl.Boolean,
                    }
                ),
            )
        else:
            # Regular variable - return scalar value
            return cast_to_dtypes(
                pl.Series([_extract_regular(v) for v in series]),
                self.var_types.get(series.name, "CONTINUOUS"),
            )

    return name.map_batches(_batch_extract)

add_interval_vars

add_interval_vars(
    name: str,
    *,
    start: float | tuple[int, int] | Expr | None = None,
    end: float | tuple[int, int] | Expr | None = None,
    duration: float | tuple[int, int] | Expr | None = None,
    length: float | tuple[int, int] | Expr | None = None,
    optional: bool | Expr = False,
) -> Expr

Create interval variables for each row in the DataFrame.

Interval variables represent tasks/activities with start time, end time, and duration. They can be optional (present/absent).

Parameters:

Name Type Description Default
name str

Name for the interval variable column

required
start int | float | tuple[int, int] | Expr | None

Start time or (min, max) bounds. If None, unbounded.

None
end int | float | tuple[int, int] | Expr | None

End time or (min, max) bounds. If None, unbounded.

None
duration int | float | tuple[int, int] | Expr | None

Duration or (min, max) bounds. If None, computed from start/end.

None
length int | float | tuple[int, int] | Expr | None

Length (actual work time, may differ from duration). If None, equals duration.

None
optional bool | Expr

Whether intervals can be absent (not scheduled)

False

Returns:

Type Description
Expr

Polars expression that creates interval variables when materialized

Examples:

>>> df = df.with_columns(
...     xmodel.add_interval_vars("task", duration=pl.col("task_duration"))
... )
Source code in src/xplor/cplex_cp/model.py
def add_interval_vars(
    self,
    name: str,
    *,
    start: float | tuple[int, int] | pl.Expr | None = None,
    end: float | tuple[int, int] | pl.Expr | None = None,
    duration: float | tuple[int, int] | pl.Expr | None = None,
    length: float | tuple[int, int] | pl.Expr | None = None,
    optional: bool | pl.Expr = False,
) -> pl.Expr:
    """Create interval variables for each row in the DataFrame.

    Interval variables represent tasks/activities with start time, end time,
    and duration. They can be optional (present/absent).

    Parameters
    ----------
    name : str
        Name for the interval variable column
    start : int | float | tuple[int, int] | pl.Expr | None, default None
        Start time or (min, max) bounds. If None, unbounded.
    end : int | float | tuple[int, int] | pl.Expr | None, default None
        End time or (min, max) bounds. If None, unbounded.
    duration : int | float | tuple[int, int] | pl.Expr | None, default None
        Duration or (min, max) bounds. If None, computed from start/end.
    length : int | float | tuple[int, int] | pl.Expr | None, default None
        Length (actual work time, may differ from duration). If None, equals duration.
    optional : bool | pl.Expr, default False
        Whether intervals can be absent (not scheduled)

    Returns
    -------
    pl.Expr
        Polars expression that creates interval variables when materialized

    Examples
    --------
    >>> df = df.with_columns(  # doctest: +SKIP
    ...     xmodel.add_interval_vars("task", duration=pl.col("task_duration"))
    ... )

    """
    return pl.map_batches(
        [
            parse_into_expr(start).alias("start"),  # ty:ignore[invalid-argument-type]
            parse_into_expr(end).alias("end"),  # ty:ignore[invalid-argument-type]
            parse_into_expr(duration).alias("duration"),  # ty:ignore[invalid-argument-type]
            parse_into_expr(length).alias("length"),  # ty:ignore[invalid-argument-type]
            parse_into_expr(optional).alias("optional"),
            pl.format(f"{name}[{{}}]", pl.row_index()).alias("var_name"),
        ],
        lambda s: self._add_interval_vars_wrapper(series_to_df(s), name),
        return_dtype=pl.Object,
    ).alias(name)

minimize_makespan

minimize_makespan(intervals: Expr | str) -> None

Set objective to minimize makespan (maximum end time).

Parameters:

Name Type Description Default
intervals Expr | str

The interval variables column name or expression

required

Examples:

>>> xmodel.minimize_makespan("task_iv")
Source code in src/xplor/cplex_cp/model.py
def minimize_makespan(self, intervals: pl.Expr | str) -> None:
    """Set objective to minimize makespan (maximum end time).

    Parameters
    ----------
    intervals : pl.Expr | str
        The interval variables column name or expression

    Examples
    --------
    >>> xmodel.minimize_makespan("task_iv")  # doctest: +SKIP

    """
    if isinstance(intervals, str):
        intervals = pl.col(intervals)

    ivars = self._materialize_interval_expr(intervals)
    # Minimize the maximum end time
    self.model.add(self.model.minimize(self.model.max([self.model.end_of(iv) for iv in ivars])))  # ty:ignore[unresolved-attribute]

optimize

optimize(**kwargs: Any) -> None

Solve the CP model.

Parameters:

Name Type Description Default
**kwargs Any

Additional parameters passed to model.solve() Common parameters: - TimeLimit: Maximum solve time in seconds - LogVerbosity: Verbosity level for solver output

{}
Source code in src/xplor/cplex_cp/model.py
def optimize(self, **kwargs: Any) -> None:
    """Solve the CP model.

    Parameters
    ----------
    **kwargs : Any
        Additional parameters passed to model.solve()
        Common parameters:
        - TimeLimit: Maximum solve time in seconds
        - LogVerbosity: Verbosity level for solver output

    """
    self.solution = self.model.solve(**kwargs)

get_objective_value

get_objective_value() -> float

Return the objective value from the solved model.

Returns:

Type Description
float

The objective value

Raises:

Type Description
ValueError

If the model has not been solved

Source code in src/xplor/cplex_cp/model.py
def get_objective_value(self) -> float:
    """Return the objective value from the solved model.

    Returns
    -------
    float
        The objective value

    Raises
    ------
    ValueError
        If the model has not been solved

    """
    if self.solution is None:
        msg = "Model has not been optimized"
        raise ValueError(msg)
    return self.solution.get_objective_values()[0]