Skip to content

Model

The founding block of any Pyoframe optimization model onto which variables, constraints, and an objective can be added.

Parameters:

Name Type Description Default
solver SUPPORTED_SOLVER_TYPES | _Solver | None

The solver to use. If None, Pyoframe will try to use whichever solver is installed (unless Config.default_solver was changed from its default value of auto).

None
solver_env dict[str, str] | None

Gurobi only: a dictionary of parameters to set when creating the Gurobi environment.

None
name str | None

The name of the model. Currently it is not used for much.

None
solver_uses_variable_names bool

If True, the solver will use your custom variable names in its outputs (e.g. during Model.write()). This can be useful for debugging .lp, .sol, and .ilp files, but may worsen performance.

False
print_uses_variable_names bool

If True, pyoframe will use your custom variables names when printing elements of the model to the console. This is useful for debugging, but may slightly worsen performance.

True
sense ObjSense | ObjSenseValue | None

Either "min" or "max". Indicates whether it's a minimization or maximization problem. Typically, this parameter can be omitted (None) as it will automatically be set when the objective is set using .minimize or .maximize.

None

Examples:

>>> m = pf.Model()
>>> m.X = pf.Variable()
>>> m.my_constraint = m.X <= 10
>>> m
<Model vars=1 constrs=1 has_objective=False solver=gurobi>

Use solver_env to, for example, connect to a Gurobi Compute Server:

>>> m = pf.Model(
...     "gurobi",
...     solver_env=dict(ComputeServer="myserver", ServerPassword="mypassword"),
... )
Traceback (most recent call last):
...
RuntimeError: Could not resolve host: myserver (code 6, command POST http://myserver/api/v1/cluster/jobs)

Methods:

Name Description
compute_IIS

Gurobi and COPT only: Computes the Irreducible Infeasible Set (IIS) of the model.

convert_to_fixed

Gurobi only: Converts a mixed integer program into a continuous one by fixing all the non-continuous variables to their solution values.

dispose

Disposes of the model and cleans up the solver environment.

optimize

Optimizes the model using your selected solver (e.g. Gurobi, HiGHS).

write

Outputs the model or the solution to a file (e.g. a .lp, .sol, .mps, or .ilp file).

Attributes:

Name Type Description
attr Container

An object that allows reading and writing model attributes.

binary_variables Generator[Variable]

Returns the model's binary variables.

constraints list[Constraint]

Returns the model's constraints.

has_objective bool

Returns whether the model's objective has been defined.

integer_variables Generator[Variable]

Returns the model's integer variables.

maximize Objective | None

Sets or gets the model's objective for maximization problems.

minimize Objective | None

Sets or gets the model's objective for minimization problems.

name str | None
objective Objective

Returns the model's objective.

params Container

An object that allows reading and writing solver-specific parameters.

poi

The underlying PyOptInterface model used to interact with the solver.

sense ObjSense | None
solver_name str
solver_uses_variable_names

Whether to pass human-readable variable names to the solver.

variables list[Variable]

Returns a list of the model's variables.

Source code in pyoframe/_model.py
def __init__(
    self,
    solver: SUPPORTED_SOLVER_TYPES | _Solver | None = None,
    solver_env: dict[str, str] | None = None,
    *,
    name: str | None = None,
    solver_uses_variable_names: bool = False,
    print_uses_variable_names: bool = True,
    sense: ObjSense | ObjSenseValue | None = None,
):
    self._poi, self.solver = Model._create_poi_model(solver, solver_env)
    self.solver_name: str = self.solver.name
    self._variables: list[Variable] = []
    self._constraints: list[Constraint] = []
    self.sense: ObjSense | None = ObjSense(sense) if sense is not None else None
    self._objective: Objective | None = None
    self._var_map = NamedVariableMapper() if print_uses_variable_names else None
    self.name: str | None = name

    self._params = Container(self._set_param, self._get_param)
    self._attr = Container(self._set_attr, self._get_attr)
    self._solver_uses_variable_names = solver_uses_variable_names

attr: Container

An object that allows reading and writing model attributes.

Several model attributes are common across all solvers making it easy to switch between solvers (see supported attributes for Gurobi, HiGHS, Ipopt), and COPT.

We additionally support all of Gurobi's attributes when using Gurobi.

Examples:

>>> m = pf.Model()
>>> m.v = pf.Variable(lb=1, ub=1, vtype="integer")
>>> m.attr.Silent = True  # Prevent solver output from being printed
>>> m.optimize()
>>> m.attr.TerminationStatus
<TerminationStatusCode.OPTIMAL: 2>

Some attributes, like NumVars, are solver-specific.

>>> m = pf.Model("gurobi")
>>> m.attr.NumConstrs
0
>>> m = pf.Model("highs")
>>> m.attr.NumConstrs
Traceback (most recent call last):
...
KeyError: 'NumConstrs'
See Also

Variable.attr for setting variable attributes and Constraint.attr for setting constraint attributes.

binary_variables: Generator[Variable]

Returns the model's binary variables.

Examples:

>>> m = pf.Model()
>>> m.X = pf.Variable(vtype=pf.VType.BINARY)
>>> m.Y = pf.Variable()
>>> len(list(m.binary_variables))
1

constraints: list[Constraint]

Returns the model's constraints.

has_objective: bool

Returns whether the model's objective has been defined.

Examples:

>>> m = pf.Model()
>>> m.has_objective
False
>>> m.X = pf.Variable()
>>> m.maximize = m.X
>>> m.has_objective
True

integer_variables: Generator[Variable]

Returns the model's integer variables.

Examples:

>>> m = pf.Model()
>>> m.X = pf.Variable(vtype=pf.VType.INTEGER)
>>> m.Y = pf.Variable()
>>> len(list(m.integer_variables))
1

maximize: Objective | None

Sets or gets the model's objective for maximization problems.

minimize: Objective | None

Sets or gets the model's objective for minimization problems.

name: str | None = name

objective: Objective

Returns the model's objective.

Raises:

Type Description
ValueError

If the objective has not been defined.

Examples:

>>> m = pf.Model()
>>> m.X = pf.Variable()
>>> m.objective
Traceback (most recent call last):
...
ValueError: Objective is not defined.
>>> m.maximize = m.X
>>> m.objective
<Objective terms=1 type=linear>
X
See Also

Model.has_objective

params: Container

An object that allows reading and writing solver-specific parameters.

See the list of available parameters for Gurobi, HiGHS, Ipopt, and COPT.

Examples:

For example, if you'd like to use Gurobi's barrier method, you can set the Method parameter:

>>> m = pf.Model("gurobi")
>>> m.params.Method = 2

poi

The underlying PyOptInterface model used to interact with the solver.

Modifying the underlying model directly is not recommended and may lead to unexpected behaviors.

sense: ObjSense | None = ObjSense(sense) if sense is not None else None

solver_name: str = self.solver.name

solver_uses_variable_names

Whether to pass human-readable variable names to the solver.

variables: list[Variable]

Returns a list of the model's variables.

compute_IIS()

Gurobi and COPT only: Computes the Irreducible Infeasible Set (IIS) of the model.

Gurobi and COPT only

This method only works with the Gurobi and COPT solver. Open an issue if you'd like to see support for other solvers.

Examples:

>>> m = pf.Model("gurobi")
>>> m.X = pf.Variable(lb=0, ub=2)
>>> m.Y = pf.Variable(lb=0, ub=2)
>>> m.bad_constraint = m.X >= 3
>>> m.minimize = m.X + m.Y
>>> m.optimize()
>>> m.attr.TerminationStatus
<TerminationStatusCode.INFEASIBLE: 3>
>>> m.bad_constraint.attr.IIS
Traceback (most recent call last):
...
RuntimeError: Unable to retrieve attribute 'IISConstr'
>>> m.compute_IIS()
>>> m.bad_constraint.attr.IIS
True
Source code in pyoframe/_model.py
@for_solvers("gurobi", "copt")
def compute_IIS(self):
    """Gurobi and COPT only: Computes the Irreducible Infeasible Set (IIS) of the model.

    !!! warning "Gurobi and COPT only"
        This method only works with the Gurobi and COPT solver. Open an issue if you'd like to see support for other solvers.

    Examples:
        >>> m = pf.Model("gurobi")
        >>> m.X = pf.Variable(lb=0, ub=2)
        >>> m.Y = pf.Variable(lb=0, ub=2)
        >>> m.bad_constraint = m.X >= 3
        >>> m.minimize = m.X + m.Y
        >>> m.optimize()
        >>> m.attr.TerminationStatus
        <TerminationStatusCode.INFEASIBLE: 3>
        >>> m.bad_constraint.attr.IIS
        Traceback (most recent call last):
        ...
        RuntimeError: Unable to retrieve attribute 'IISConstr'
        >>> m.compute_IIS()
        >>> m.bad_constraint.attr.IIS
        True
    """
    self.poi.computeIIS()

convert_to_fixed() -> None

Gurobi only: Converts a mixed integer program into a continuous one by fixing all the non-continuous variables to their solution values.

Gurobi only

This method only works with the Gurobi solver. Open an issue if you'd like to see support for other solvers.

Examples:

>>> m = pf.Model("gurobi")
>>> m.X = pf.Variable(vtype=pf.VType.BINARY, lb=0)
>>> m.Y = pf.Variable(vtype=pf.VType.INTEGER, lb=0)
>>> m.Z = pf.Variable(lb=0)
>>> m.my_constraint = m.X + m.Y + m.Z <= 10
>>> m.maximize = 3 * m.X + 2 * m.Y + m.Z
>>> m.optimize()
>>> m.X.solution, m.Y.solution, m.Z.solution
(1, 9, 0.0)
>>> m.my_constraint.dual
Traceback (most recent call last):
...
RuntimeError: Unable to retrieve attribute 'Pi'
>>> m.convert_to_fixed()
>>> m.optimize()
>>> m.my_constraint.dual
1.0

Only works for Gurobi:

>>> m = pf.Model("highs")
>>> m.convert_to_fixed()
Traceback (most recent call last):
...
NotImplementedError: Method 'convert_to_fixed' is not implemented for solver 'highs'.
Source code in pyoframe/_model.py
@for_solvers("gurobi")
def convert_to_fixed(self) -> None:
    """Gurobi only: Converts a mixed integer program into a continuous one by fixing all the non-continuous variables to their solution values.

    !!! warning "Gurobi only"
        This method only works with the Gurobi solver. Open an issue if you'd like to see support for other solvers.

    Examples:
        >>> m = pf.Model("gurobi")
        >>> m.X = pf.Variable(vtype=pf.VType.BINARY, lb=0)
        >>> m.Y = pf.Variable(vtype=pf.VType.INTEGER, lb=0)
        >>> m.Z = pf.Variable(lb=0)
        >>> m.my_constraint = m.X + m.Y + m.Z <= 10
        >>> m.maximize = 3 * m.X + 2 * m.Y + m.Z
        >>> m.optimize()
        >>> m.X.solution, m.Y.solution, m.Z.solution
        (1, 9, 0.0)
        >>> m.my_constraint.dual
        Traceback (most recent call last):
        ...
        RuntimeError: Unable to retrieve attribute 'Pi'
        >>> m.convert_to_fixed()
        >>> m.optimize()
        >>> m.my_constraint.dual
        1.0

        Only works for Gurobi:

        >>> m = pf.Model("highs")
        >>> m.convert_to_fixed()
        Traceback (most recent call last):
        ...
        NotImplementedError: Method 'convert_to_fixed' is not implemented for solver 'highs'.
    """
    self.poi._converttofixed()

dispose()

Disposes of the model and cleans up the solver environment.

When using Gurobi compute server, this cleanup will ensure your run is not marked as 'ABORTED'.

Note that once the model is disposed, it cannot be used anymore.

Examples:

>>> m = pf.Model()
>>> m.X = pf.Variable(ub=1)
>>> m.maximize = m.X
>>> m.optimize()
>>> m.X.solution
1.0
>>> m.dispose()
Source code in pyoframe/_model.py
def dispose(self):
    """Disposes of the model and cleans up the solver environment.

    When using Gurobi compute server, this cleanup will
    ensure your run is not marked as 'ABORTED'.

    Note that once the model is disposed, it cannot be used anymore.

    Examples:
        >>> m = pf.Model()
        >>> m.X = pf.Variable(ub=1)
        >>> m.maximize = m.X
        >>> m.optimize()
        >>> m.X.solution
        1.0
        >>> m.dispose()
    """
    env = None
    if hasattr(self.poi, "_env"):
        env = self.poi._env
    self.poi.close()
    if env is not None:
        env.close()

optimize()

Optimizes the model using your selected solver (e.g. Gurobi, HiGHS).

Source code in pyoframe/_model.py
def optimize(self):
    """Optimizes the model using your selected solver (e.g. Gurobi, HiGHS)."""
    self.poi.optimize()

write(file_path: Path | str, pretty: bool = False)

Outputs the model or the solution to a file (e.g. a .lp, .sol, .mps, or .ilp file).

These files can be useful for manually debugging a model. Consult your solver documentation to learn more.

When creating your model, set solver_uses_variable_names to make the outputed file human-readable.

m = pf.Model(solver_uses_variable_names=True)

For Gurobi, solver_uses_variable_names=True is mandatory when using .write(). This may become mandatory for other solvers too without notice.

Parameters:

Name Type Description Default
file_path Path | str

The path to the file to write to.

required
pretty bool

Only used when writing .sol files in HiGHS. If True, will use HiGH's pretty print columnar style which contains more information.

False
Source code in pyoframe/_model.py
def write(self, file_path: Path | str, pretty: bool = False):
    """Outputs the model or the solution to a file (e.g. a `.lp`, `.sol`, `.mps`, or `.ilp` file).

    These files can be useful for manually debugging a model.
    Consult your solver documentation to learn more.

    When creating your model, set [`solver_uses_variable_names`][pyoframe.Model]
    to make the outputed file human-readable.

    ```python
    m = pf.Model(solver_uses_variable_names=True)
    ```

    For Gurobi, `solver_uses_variable_names=True` is mandatory when using
    .write(). This may become mandatory for other solvers too without notice.

    Parameters:
        file_path:
            The path to the file to write to.
        pretty:
            Only used when writing .sol files in HiGHS. If `True`, will use HiGH's pretty print columnar style which contains more information.
    """
    if not self.solver.supports_write:
        raise NotImplementedError(f"{self.solver.name} does not support .write()")
    if (
        not self.solver_uses_variable_names
        and self.solver.accelerate_with_repeat_names
    ):
        raise ValueError(
            f"{self.solver.name} requires solver_uses_variable_names=True to use .write()"
        )

    file_path = Path(file_path)
    file_path.parent.mkdir(parents=True, exist_ok=True)

    kwargs = {}
    if self.solver.name == "highs":
        if self.solver_uses_variable_names:
            self.params.write_solution_style = 1
        kwargs["pretty"] = pretty
    self.poi.write(str(file_path), **kwargs)