Model

Model is the central class of the PyOptInterface package. It provides the interface to the solver and the user. The user can add variables, constraints and the objective function to the model. The model can be solved and the solution can be queried.

In this document we will only discuss the common interface of the model. For solver-specific interface, please refer to the documentation of the corresponding solver.

Create a model

A model is a concrete instance tied to a specific solver. To create a model, we need to import the corresponding module and call the constructor of the model class. For example, to create a HiGHS model, we can do:

import pyoptinterface as poi
from pyoptinterface import highs

model = highs.Model()

You can replace highs with the name of the solver you want to use. The available solvers includes copt, gurobi, highs and mosek.

Most commercial solvers require a Environment-like object to be initialized before creating a model in order to manage the license.

By default, PyOptInterface creates the global environment for each solver. If you want to create a model in a specific environment, you can pass the environment object to the constructor of the model class. The details can be found on the documentation of the corresponding optimizer: Gurobi, HiGHS, COPT, MOSEK.

env = gurobi.Env()
model = gurobi.Model(env)

Inspect and customize the model

We can query and modify the parameters of the model to manipulate the behavior of the underlying solver.

Like the design in JuMP.jl, we define a small subset of parameters that are common to all solvers. They are defined as pyoptinterface.ModelAttribute enum class. The meanings of these standard attributes are the same as Model attributes and Optimizer attributes in MathOptInterface.jl.

Standard model attributes

Attribute name

Type

Name

str

ObjectiveSense

ObjectiveSense

DualStatus

ResultStatusCode

PrimalStatus

ResultStatusCode

RawStatusString

str

TerminationStatus

TerminationStatusCode

BarrierIterations

int

DualObjectiveValue

float

NodeCount

int

NumberOfThreads

int

ObjectiveBound

float

ObjectiveValue

float

RelativeGap

float

Silent

float

SimplexIterations

int

SolverName

str

SolverVersion

str

SolveTimeSec

float

TimeLimitSec

float

We can set the value of a parameter by calling the set_model_attribute method of the model:

# suppress the output of the solver
model.set_model_attribute(poi.ModelAttribute.Silent, False)
# set the time limit to 10 seconds
model.set_model_attribute(poi.ModelAttribute.TimeLimitSec, 10.0)

The value of parameter can be queried by calling the get_model_attribute method of the model.

For example, we build and solve a simple quadratic programming model, then query the objective value of the model:

x = model.add_variables(range(2), lb=0.0, ub=1.0)
model.add_linear_constraint(x[0] + x[1], poi.Eq, 1.0)

model.set_objective(x[0]*x[0] + x[1]*x[1], sense=poi.ObjectiveSense.Minimize)

model.optimize()

objval = model.get_model_attribute(poi.ModelAttribute.ObjectiveValue)

print(f"Objective value: {objval}")
Running HiGHS 1.8.1 (git hash: 4a7f24a): Copyright (c) 2024 HiGHS under MIT licence terms
Coefficient ranges:
  Matrix [1e+00, 1e+00]
  Cost   [0e+00, 0e+00]
  Bound  [1e+00, 1e+00]
  RHS    [1e+00, 1e+00]
  Iteration        Objective     NullspaceDim
          0                1                0      0.00s
          2       0.50000002                1      0.00s
Model status        : Optimal
QP ASM    iterations: 2
Objective value     :  5.0000000000e-01
HiGHS run time      :          0.00
Objective value: 0.49999999999999994

Besides the standard attributes, we can also set/get the solver-specific attributes by calling the set_raw_parameter/get_raw_parameter method of the model:

# Gurobi
model.set_raw_parameter("OutputFlag", 0)
# COPT
model.set_raw_parameter("Presolve", 0)
# MOSEK
model.set_raw_parameter("MSK_IPAR_INTPNT_BASIS", 0)

Solve the model

We can solve the model by calling the optimize method of the model:

model.optimize()

Query the solution

We can query the termination status of the model after optimization by query the TerminationStatus attribute of the model:

# tell if the optimizer obtains the optimal solution
termination_status = model.get_model_attribute(poi.ModelAttribute.TerminationStatus)
assert termination_status == poi.TerminationStatusCode.OPTIMAL

For value of variables and expressions, we can use get_value method of the model:

x0_value = model.get_value(x[0])
expr_value = model.get_value(x[0]*x[0])

print(f"x[0] = {x0_value}")
print(f"x[0]^2 = {expr_value}")
x[0] = 0.49999999999999994
x[0]^2 = 0.24999999999999994

Write the model to file

The optimization model can be written to file in LP, MPS or other formats. The write method of the model can be used to write the model to file:

model.write("model.lp")
Writing the model to model.lp

The file format is determined by the file extension. Because we use the native IO procedure of the optimizer, their supported file formats and the content of output files may vary. Please refer to the documentation of the corresponding optimizer for more details.