gurobi速查手册
Gurobi中一些基本的概念
Parameters:参数用于控制求解器的行为,相关参数参考。Attributes:属性是查询和修改相关对象特征的机制,相关属性参考。Environment:环境是Model的容器,gurobi的python接口会自动创建环境。设置环境的Parameters会让环境中的每个Model都默认这些Parameters,也可以单独为每个模型设置Parameters。
使用Gurobi建模的一般步骤
使用Gurobi求解优化模型比较有条理的步骤如下:
- 先建模,建模可按以下步骤一步一步分析,避免脑袋绕晕
- 定义模型中要使用的集合
- 定义模型中要使用的参数
- 定义模型中要使用的决策变量
- 定义模型的目标函数
- 分析模型应该包含的所有约束条件
- 然后使用Gurobi求解,具体又可按以下步骤进行
- 使用list或tuplelist对象保存集合(附:集合在模型中一般用作索引)
- 使用dict或tupledict对象保存参数
- 创建模型对象
- 添加变量
- 设置目标函数
- 添加约束条件
- 进行优化
tuplelist和tupledict数据结构与list和dict的区别是什么?
前者是gurobi的内置数据结构,有更加强大的索引功能。
操作速查
获取或修改模型的参数
直接用Model对象的Params属性就可以实现gurobi中相关参数的获取和修改。
- Termination parameters which set the stopping conditions for algorithms;
- Tolerance parameters which control solution quality requirements;
- Logging and I/O parameters which control logging behavior as well as input and output files;
- Algorithmic control parameters for Presolve, Simplex, Barrier, Scaling, MIP, MIP Cuts, and Numerics;
- Tuning parameters which control parameter tuning strategies for grbtune and the tune API calls;
- Solution Pool parameters which control how the solver searches for and stores additional solutions other than the best available one;
- Multi-Objective parameters which control some aspects when working with multiple objectives;
- Parameters for configuring aspects of remote optimization and licensing: Parallel and Distributed Computing, Instant Cloud, Compute Server, Cluster Manager, Token Server, Web License Service; and
通过Model对象的Params属性修改参数的示范model.Params.MIPFocus = 0 # 求解策略参数的设置 model.Params.LogFile = 'modelversion2.log' # 求解日志存放位置 model.Params.MIPGap = 0.7 # 要求找到最优解,*测试的时候直接设置称60%的gap,真正要收敛到0太困难了,收到60%左右基本上就是最优解,利用这个技巧方便后面做灵敏度分析*
获取或修改对象的属性
获取对象属性的话就用Model对象的getAttr方法就好了,设置对象属性则可以使用setAttr方法,当然了也可以使用面向对象的.属性名方式来访问或修改属性,缺点是批量操作得写循环。这两个方法都有attrname和objects参数,当只提供属性名的时候,查询或设置的就是Model对象对应属性的值,如果提供了objects(通常是tuplelist或tupledict)这两个方法就查询和设置的是这些objects的属性值。
gurobi获取或删除对象属性的示范main_model.setAttr('VType',main_model.getVars(),GRB.INTEGER) # 将main_model.getVars()获取到的所有变量的vtype属性设置为GRB.INTEGER。
使用quicksum函数和tupledict的sum方法完成求和操作
quicksum一般的用法是里面写一个生成器推导式,得到所有要求和的项,与sum方法相比,quicksum函数更加灵活,可以实现变量与系数的加权求和。
quicksum函数和sum方法的示例for i in I: model.addConstr(gp.quicksum(x[i,j] for j in J)<=5) for i in I: model.addConstr(x.sum(i,"*")<=5)
创建约束条件的时候实际上可以使用一种更加简单的写法,及在addConstrs方法内部写生成器推导式。上述的约束条件也可以写成:
另一种简便写法model.addConstrs(gp.quicksum(x[i,j] for I in J)<=5 for i in I) model.addConstrs(x.sum(i,"*") for i in I)
利用GRB减少不必要的记忆
GRB类中定义了Gurobi中常用的一些常量,例如我想将求解策略参数设置为均衡模式,我当然可以写model.Params.MIPFocus = 0,但是0这个数字并没什么字面意义,时间一长你可能就忘了到底得把MIPFocus设置成几才是均衡模式了。使用GRB类中定义的常量你可以写成model.Params.MIPFocus=MIPFOCUS_BALANCED。
GRB类中定义的常量# Status codes LOADED = 1 OPTIMAL = 2 INFEASIBLE = 3 INF_OR_UNBD = 4 UNBOUNDED = 5 CUTOFF = 6 ITERATION_LIMIT = 7 NODE_LIMIT = 8 TIME_LIMIT = 9 SOLUTION_LIMIT = 10 INTERRUPTED = 11 NUMERIC = 12 SUBOPTIMAL = 13 INPROGRESS = 14 USER_OBJ_LIMIT = 15 WORK_LIMIT = 16 MEM_LIMIT = 17 # Batch status codes BATCH_CREATED = 1 BATCH_SUBMITTED = 2 BATCH_ABORTED = 3 BATCH_FAILED = 4 BATCH_COMPLETED = 5 # Constraint senses LESS_EQUAL = '<' GREATER_EQUAL = '>' EQUAL = '=' # Variable types CONTINUOUS = 'C' BINARY = 'B' INTEGER = 'I' SEMICONT = 'S' SEMIINT = 'N' # Objective sense MINIMIZE = 1 MAXIMIZE = -1 # SOS types SOS_TYPE1 = 1 SOS_TYPE2 = 2 # General constraint types GENCONSTR_MAX = 0 GENCONSTR_MIN = 1 GENCONSTR_ABS = 2 GENCONSTR_AND = 3 GENCONSTR_OR = 4 GENCONSTR_NORM = 5 GENCONSTR_NL = 6 GENCONSTR_INDICATOR = 7 GENCONSTR_PWL = 8 GENCONSTR_POLY = 9 GENCONSTR_EXP = 10 GENCONSTR_EXPA = 11 GENCONSTR_LOG = 12 GENCONSTR_LOGA = 13 GENCONSTR_POW = 14 GENCONSTR_SIN = 15 GENCONSTR_COS = 16 GENCONSTR_TAN = 17 GENCONSTR_LOGISTIC = 18 # Operation codes OPCODE_CONSTANT = 0 OPCODE_VARIABLE = 1 OPCODE_PLUS = 2 OPCODE_MINUS = 3 OPCODE_MULTIPLY = 4 OPCODE_DIVIDE = 5 OPCODE_UMINUS = 6 OPCODE_SQUARE = 7 OPCODE_SQRT = 8 OPCODE_SIN = 9 OPCODE_COS = 10 OPCODE_TAN = 11 OPCODE_POW = 12 OPCODE_EXP = 13 OPCODE_LOG = 14 OPCODE_LOG2 = 15 OPCODE_LOG10 = 16 OPCODE_LOGISTIC = 17 # Basis status BASIC = 0 NONBASIC_LOWER = -1 NONBASIC_UPPER = -2 SUPERBASIC = -3 # Numeric constants INFINITY = 1e100 UNDEFINED = 1e101 MAXINT = 2000000000 # Limits MAX_NAMELEN = 255 MAX_STRLEN = 512 MAX_TAGLEN = 10240 MAX_CONCURRENT = 64 # Other constants DEFAULT_CS_PORT = 61000 # Version number VERSION_MAJOR = 12 VERSION_MINOR = 0 VERSION_TECHNICAL = 0 # Errors ERROR_OUT_OF_MEMORY = 10001 ERROR_NULL_ARGUMENT = 10002 ERROR_INVALID_ARGUMENT = 10003 ERROR_UNKNOWN_ATTRIBUTE = 10004 ERROR_DATA_NOT_AVAILABLE = 10005 ERROR_INDEX_OUT_OF_RANGE = 10006 ERROR_UNKNOWN_PARAMETER = 10007 ERROR_VALUE_OUT_OF_RANGE = 10008 ERROR_NO_LICENSE = 10009 ERROR_SIZE_LIMIT_EXCEEDED = 10010 ERROR_CALLBACK = 10011 ERROR_FILE_READ = 10012 ERROR_FILE_WRITE = 10013 ERROR_NUMERIC = 10014 ERROR_IIS_NOT_INFEASIBLE = 10015 ERROR_NOT_FOR_MIP = 10016 ERROR_OPTIMIZATION_IN_PROGRESS = 10017 ERROR_DUPLICATES = 10018 ERROR_NODEFILE = 10019 ERROR_Q_NOT_PSD = 10020 ERROR_QCP_EQUALITY_CONSTRAINT = 10021 ERROR_NETWORK = 10022 ERROR_JOB_REJECTED = 10023 ERROR_NOT_SUPPORTED = 10024 ERROR_EXCEED_2B_NONZEROS = 10025 ERROR_INVALID_PIECEWISE_OBJ = 10026 ERROR_UPDATEMODE_CHANGE = 10027 ERROR_CLOUD = 10028 ERROR_MODEL_MODIFICATION = 10029 ERROR_CSWORKER = 10030 ERROR_TUNE_MODEL_TYPES = 10031 ERROR_SECURITY = 10032 ERROR_NOT_IN_MODEL = 20001 ERROR_FAILED_TO_CREATE_MODEL = 20002 ERROR_INTERNAL = 20003 # Cuts parameter values CUTS_AUTO = -1 CUTS_OFF = 0 CUTS_CONSERVATIVE = 1 CUTS_AGGRESSIVE = 2 CUTS_VERYAGGRESSIVE = 3 # Presolve parameter values PRESOLVE_AUTO = -1 PRESOLVE_OFF = 0 PRESOLVE_CONSERVATIVE = 1 PRESOLVE_AGGRESSIVE = 2 # Method parameter values METHOD_NONE = -1 METHOD_AUTO = -1 METHOD_PRIMAL = 0 METHOD_DUAL = 1 METHOD_BARRIER = 2 METHOD_CONCURRENT = 3 METHOD_DETERMINISTIC_CONCURRENT = 4 METHOD_DETERMINISTIC_CONCURRENT_SIMPLEX = 5 # BarHomogeneous parameter values BARHOMOGENEOUS_AUTO = -1 BARHOMOGENEOUS_OFF = 0 BARHOMOGENEOUS_ON = 1 # BarOrder parameter values BARORDER_AUTOMATIC = -1 BARORDER_AMD = 0 BARORDER_NESTEDDISSECTION = 1 # MIPFocus parameter values MIPFOCUS_BALANCED = 0 MIPFOCUS_FEASIBILITY = 1 MIPFOCUS_OPTIMALITY = 2 MIPFOCUS_BESTBOUND = 3 # SimplexPricing parameter values SIMPLEXPRICING_AUTO = -1 SIMPLEXPRICING_PARTIAL = 0 SIMPLEXPRICING_STEEPEST_EDGE = 1 SIMPLEXPRICING_DEVEX = 2 SIMPLEXPRICING_STEEPEST_QUICK = 3 # VarBranch parameter values VARBRANCH_AUTO = -1 VARBRANCH_PSEUDO_REDUCED = 0 VARBRANCH_PSEUDO_SHADOW = 1 VARBRANCH_MAX_INFEAS = 2 VARBRANCH_STRONG = 3 # Partition parameter values PARTITION_EARLY = 16 PARTITION_ROOTSTART = 8 PARTITION_ROOTEND = 4 PARTITION_NODES = 2 PARTITION_CLEANUP = 1 # Callback phase values PHASE_MIP_NOREL = 0 PHASE_MIP_SEARCH = 1 PHASE_MIP_IMPROVE = 2 # FeasRelax method parameter values FEASRELAX_LINEAR = 0 FEASRELAX_QUADRATIC = 1 FEASRELAX_CARDINALITY = 2
通过回调函数插入惰性约束
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2025-05-04 10:26:02
# @Author : syuansheng (Dalian Maritime University)
from math import sqrt
from typing import Dict,List
from numpy import ndarray,zeros
from gurobipy import Model,GRB,GurobiError,quicksum
def findAllSubroutes(solution:ndarray)->Dict[int,List[List]]:
route_dict = {} # subtours of each car
for k in range(solution.shape[2]):
subtour_kcars = [] # subtours of car k
# check subtours of car k
for i in range(1,solution.shape[1]-1):
flag = False
subtour_kcars_start_i = [] # subtours of car k that starts from point i
for j in range(1,solution.shape[1]-1):
# car k moved from point i to j
if solution[i,j,k] > 0.9:
subtour_kcars_start_i.append(i)
subtour_kcars_start_i.append(j)
current_point = j
# cheack whether i->j formed a subtour
while not flag:
for next_point in range(1,solution.shape[1]):
if solution[current_point,next_point,k] > 0.9:
subtour_kcars_start_i.append(next_point)
current_point = next_point
# we find a subtour start from ponstallint i
if subtour_kcars_start_i[0] == subtour_kcars_start_i[-1]:
flag = True
subtour_kcars.append(subtour_kcars_start_i)
break
# car k finally arrived point (solution.shape[2]-1) means i->j didn't form a subtour
if current_point == solution.shape[1]-1:
flag = True
break
else:
continue
route_dict[k] = subtour_kcars
return route_dict
class Data:
def __init__(self):
self.o = 0 # index of the depot
self.d = 16 # index of the depot(duplication)
self.C = [i for i in range(1,16)] # index of each customer
self.V = [self.o]+self.C+[self.d]
self.K = [i for i in range(3)] # index of each car
self.A = [] # all valid arc
self.Q = 100 # capacity of each car
self.c = {} # distance between i to j
self.q = {} # demand of each customer
self.loc = {} # cordinate of each point
def getDataReady(self):
lst = []
with open(r'./r101_CVRP.txt') as f:
str_lst = f.readlines()
for line in str_lst:
lst.append(list(map(int,line.strip().split())))
for i,item in enumerate(lst):
self.loc[i] = [item[1],item[2]]
self.q[i] = item[3]
for i in self.V:
for j in self.V:
# exclude invalid arc
if i == self.d or j == self.o or i == j or (i == self.o and j == self.d):
continue
else:
self.A.append((i,j))
for i,j in self.A:
if j == self.d:
self.c[i,j] = round(sqrt((self.loc[i][0]-self.loc[self.o][0])**2+(self.loc[i][1]-self.loc[self.o][1])**2),2)
else:
self.c[i,j] = round(sqrt((self.loc[i][0]-self.loc[j][0])**2+(self.loc[i][1]-self.loc[j][1])**2),2)
class VRPModel:
def __init__(self,data):
self._model = None
self._data = data
self.x = {}
def buildModel(self):
self._model = Model(name='CVRP1_1')
self.x = self._model.addVars(self._data.A,self._data.K,vtype=GRB.BINARY,name='x')
# set objective function
self._model.setObjective(
quicksum(self._data.c[i,j]*self.x[i,j,k] for i,j in self._data.A for k in self._data.K),
sense=GRB.MINIMIZE
)
# set constraints
self._model.addConstrs(
(self.x.sum(i,'*','*')==1 for i in self._data.C),
name = 'constraint1'
)
self._model.addConstrs(
(self.x.sum(self._data.o,'*',k)==self.x.sum('*',self._data.d,k) for k in self._data.K),
name = 'constraint2'
)
self._model.addConstrs(
(self.x.sum('*',self._data.d,k)<=1 for k in self._data.K),
name = 'constraint3'
)
self._model.addConstrs(
(self.x.sum(i,'*',k)==self.x.sum('*',i,k) for i in self._data.C for k in self._data.K),
name = 'constraint4'
)
self._model.addConstrs(
(quicksum(self._data.q[i]*self.x[i,j,k] for i,j in self._data.A)<=self._data.Q for k in self._data.K),
name = 'constraint5'
)
def solveModel(self):
try:
self._model.Params.MIPGap = 0
self._model.Params.lazyConstraints = 1
self._model.optimize(self.generateDFJConstraint)
except GurobiError as e:
print('erro number:{},erro string:{}'.format(e.errno,e.message))
def generateDFJConstraint(self,model,where):
# each time when gurobi find a feasible solution,we check whether we should add some DFJ constraints
if where == GRB.Callback.MIPSOL:
solution = zeros((len(self._data.V),len(self._data.V),len(self._data.K))) # save the solution results in matrix format
for key,value in self.x.items():
idx1,idx2,idx3 = key
solution[idx1,idx2,idx3] = self._model.cbGetSolution(value)
route_dict = findAllSubroutes(solution)
for k in self._data.K:
for subtour_kcars_start_i in route_dict[k]:
# Now we get the set S(subtour_kcars_start_i[:-1])
self._model.cbLazy(
quicksum(self.x[i,j,k] for i,j in self._data.A if (i in subtour_kcars_start_i[:-1] and j in subtour_kcars_start_i[:-1]))
<= len(subtour_kcars_start_i[:-1])-1
)
if __name__ == '__main__':
data = Data()
data.getDataReady()
model = VRPModel(data)
model.buildModel()
model.solveModel()
Gurobi实现列生成算法
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2025-07-25 14:34:23
# @Author : syuansheng (Dalian Maritime University)
from gurobipy import *
class Data:
def __init__(self):
self.l = [4,5,7] # 需求类型
self.L = [9,14,16] # 可用原木的长度
self.c = [5,9,10] # 各种长度原木的价格
class CGModel:
def __init__(self):
self.data = Data()
# 初始化主模型
y = self.initiate_main_model()
# 初始化所有子模型
self.initiate_sub_model(y)
def initiate_main_model(self):
self.main_model = Model()
self.x = self.main_model.addVars(3,vtype=GRB.CONTINUOUS,name='x')
self.main_model.setObjective(
5*self.x[0]+5*self.x[1]+5*self.x[2],
sense = GRB.MINIMIZE
)
self.main_model.addConstr(2*self.x[0]>=30)
self.main_model.addConstr(self.x[1]>=20)
self.main_model.addConstr(self.x[2]>=40)
self.main_model.optimize()
# 获取对偶变量的值
y = self.main_model.getAttr('Pi',self.main_model.getConstrs())
return y
def initiate_sub_model(self,y):
self.submodels = []
for k in range(len(self.data.L)):
# k子模型
submodel = Model()
p = submodel.addVars(3,vtype=GRB.INTEGER,name='p')
submodel.setObjective(
self.data.c[k]-quicksum(y[i]*p[i] for i in range(3)),
sense=GRB.MINIMIZE
)
submodel.addConstr(quicksum(p[i]*self.data.l[i] for i in range(3))<=self.data.L[k])
self.submodels.append(submodel)
def update(self):
"""
不断更新主模型和子模型并求解,直到得到原模型的最优解
"""
while True:
best_ObjVal,best_P,submodel_idx = 0,None,0
# 求解子模型
for k,submodel in enumerate(self.submodels):
submodel.optimize()
if submodel.ObjVal < best_ObjVal:
# 说明存在列加入主模型后使得主模型更好
best_ObjVal = submodel.ObjVal # 子模型最优解目标函数值
best_P = submodel.X # 子模型最优解
submodel_idx = k
# 退出
if best_ObjVal >= 0 :
break
# 将新的列添加到主模型中,求解主模型并更新子模型
else:
col = Column(best_P,self.main_model.getConstrs()) # 创建要加入到主模型的列
self.main_model.addVar(obj=self.data.c[submodel_idx],column=col,vtype=GRB.CONTINUOUS,name='new_x') # 将新的列加入主模型
self.main_model.optimize() # 求解主模型
self.main_model.write('ttest.lp')
y = self.main_model.getAttr('Pi',self.main_model.getConstrs()) # 新的对偶变量值
print('----------------------------',y,best_P,submodel_idx)
self.initiate_sub_model(y) # 更新子模型
# 将模型转换为对应的IP模型继续求解
self.main_model.setAttr('VType',self.main_model.getVars(),GRB.INTEGER)
self.main_model.optimize()
if __name__ == '__main__':
model = CGModel()
model.update()
print(model.main_model.ObjVal)
print(model.main_model.X)
参考资料
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.






.png)