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.
Attribute name |
Type |
---|---|
Name |
str |
ObjectiveSense |
|
DualStatus |
|
PrimalStatus |
|
RawStatusString |
str |
TerminationStatus |
|
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.7.2 (git hash: 5ce7a27): Copyright (c) 2024 HiGHS under MIT licence terms
Objective value: 0.49999999999999994
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
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.