callback函数的主要作用是为了获取程序运行过程中的一些中间信息,或者在程序运行过程中动态修改程序运行状态,如用户有时在求解过程中需要实现一些功能,包括终止优化、添加约束条件(割平面)、嵌人自己的算法等。
一、回调函数callback定义
回调雨数callback的定义的方法如下
deffuncionname(model,where):
print('dosomethingwheregurobirun')
其中callback函数有两个固定的参数:model是指定义的gurobi.Model类,where是指回调函数的出发点。
在callback函数使用过程中,需要注意的是where和what,即在什么地方(where)获取哪些信息(what),如下面的代码,cbGet查询获取优化器的指定信息,即grb.CRB.Callback.MUL,TIOBJ_OBJCNT当前解的数量。
ifwhere==grb.GRB.Callback.MULTIOBJ:#where
print(model.cbGet(grb.GRB.Callback.MULTIOBJOBJCNT))#what
注意:where和what一般是配套使用的,如当where=MIP时,what只能获取MIP的相关信息。
二、状态where与值what
下面来观察callback函数的where取值有哪些如表所示。

当where=grb.GRB.Callback.MIP时,what可以取表中的值。

当where=grb.GRB.Callback.MIPSOL时,what可以取表中的值。

更多关于callback雨数的选项可以参考《Gurobi优化器开发参考手册》的相关信息。
三、callback函数的功能
在Gurobi中除cbGet函数外还有一些常用函数用于获取运行过程中信息或修改运行状态,包括cbGetNodeRel,cbGetSolution,cbCut,cblazy,cbSetSolution,cbStopOneMultiObj等。
1.cbGet(what)
这个函数的使用最为频繁,常用于查询求解过程中的一些信息,如目标值、节点数等,使用时应注意what与where的匹配,如代码所示。
importgurobipyasgrb
model=grb.Model()
#查询查询当前单纯形的目标函数值
defmycallback(model,where):
ifwhere==grb.GRB.Callback.SIMPLEX:
print(model.cbGet(grb.GRB.Callback.SPX_OBJVAL))
model.optimize(mycallback)
2.cbGetNodeRel(vars)
这个函数用来查询变量在当前节点的松弛解。Vars为要查询的变量,需要注意的是,只有在where==GRB.Callback.MIPNODE并且GRB.Callback.MIPNODESTATUS==CRB.OPTIMAL两个条件同时成立时才能使用,如代码所示。
importgurobipyasgrb
model=grb.Model()
#查询变量在当前节点的松弛解
defmycallback(model,where):
ifwhere==grb.GRB.Callback.MIPNODEand\
model.cbGet(grb.GRB.Callback.MIPNODE_STATUS)==grb.GRB.OPTIMAL:
print(model.cbGetNodeRel(model._vars))
model._vars=model.getVars()
model.optimize(mycallback)
3.cbGetSolution(vars)
这个函数用于在MIP问题中查询变量在新可行解中的值,需要注意的是,只有在where==CRB.
Callback.MIPSOL或GRB.CallbackMULTIOBJ时才能使用,如代码所示。
importgurobipyasgrb
model=grb.Model()
#在MIP问题中查询变量在新可行解中的值
defmycallback(model,where):
ifwhere==grb.GRB.Callback.MIPSOL:
print(model.cbGetSolution(model._vars))
model._vars=model.getVars()
model.optimize(mycallback)
4.cbCut(lhs,sense,rhs)
这个函数用于求解MIP问题时,在节点中添加制平面。需要注意要的是,只有在where==CRB.Callback.MIPNODE时才能使用。虽然制平面可以添加到树枝和切割树的任何节点,但是它们会增加在每个节点处求解松弛模型的大小,并且会显著降低节点处理的速度,因此应谨慎添加。割平面通常用于切断当前松弛解,要在当前节点上检索松弛解决方案,应首先调用cbGetNodeRel。添加自定义割平面,必须将参数precrush的值设置为1。
cbCut的3个参数表示割平面,其实就是一个约束条件,根据gurobi的语法,约束一般有两种写法如下所示。
(1)model.cbCut(x+y+z<=3).
(2)model.cbCut([x,y,z],grb.GRB.LESS_EQUAL,3)。
因此,lhs表示约束的左边的xy+z,sense表示约束是大于等于还是小于,rhs表示约束的右边。
通过节点的松弛解信息来构造割平面,如代码所示。
importgurobipyasgrb
model=grb.Model()
#在求解MIP问题时在节点添加割平面
defmycallback(model,where):
ifwhere==grb.GRB.Callback.MIPNODE:
status=model.cbGet(grb.GRB.Callback.MIPNODE_STATUS)
ifstatus==grb.GRB.OPTIMAL:
rel=model.cbGetNodeRel([model._vars[0],model._vars[1]])
ifrel[0]+rel[1]>1.1:
model.cbCut(model._vars[0]+model._vars[1]<=1)
model._vars=model.getVars()
model.Params.PreCrush=1
model.optimize(mycallback)
5.cbLazy(lhs,sense,rhs)
这个函数用于MIP问题的求解过程中,在节点添加Lazycut。需要注意的是,只有在where==GRB.Callback.MIPNODE或where==CRB.CallbackMIPSOL时才起作用。当MIP模型的完整约束集太大而无法显式表示时,通常使用惰性约束。通过只包含在分支割平面搜索过程中不满足条件的约束有时也可以在只添加完整约束集的一小部分时找到经验证的最优解。在添加Lazycut之前应该先查询当前节点的解(通过chGetSolution获取CRB.CB_MIPSOL或通过cbGetNodeRel获取CRB.CB_MIP-NODE)
下面演示通过可行解的信息来添加Lazycut,如代码所示。
importgurobipyasgrb
model=grb.Model()
#添加lazycut
defmycallback(model,where):
ifwhere==grb.GRB.Callback.MIPSOL:
sol=model.cbGetSolution([model._vars[0],model._vars[1]])
ifsol[0]+sol[1]>1.1:
model.cbLazy(model._vars[0]+model._vars[1]<=1)
model._vars=model.getVars()
model.Params.lazyConstraints=1
model.optimize(mycallback)
6.cbSetSolution(vars,solution)
这个函数用于向当前节点导人一个解,这个解既可以是完整的,也可以是部分的。需要注意的是,只有当where==RB.CB_MIPNODE时才起作用。如果使用启发式算法作为模型的一个初始解可以使用这个函数,但对于复杂的问题,应先用启发式算法找到一个可行解,然后再导人让Gurobi在其基础上继续求解。如果要指定多组变量的值,也可以多次调用这个方法。不仅可以从一个回调函数中多次调用cbsetSolution雨数以指定多组变量的值,也可以在同调函数中使用chUseSolotion函数尝达从指定的值开始计算可行解,如代码所示。
importgurobipyasgrb
model=grb.Model()
#从当前节点导入解
defmycallback(model,where):
ifwhere==grb.GRB.Callback.MIPNODE:
model.cbSetSolution(vars,newsolution)
model.optimize(mycallback)
7.cbStopOneMultiObj(objcnt)
这个函数用于在非分层优化的多目标MIP问题中,中断其优化过程。需要注意的是,只有在多目标MIP问题中且where==GRB.Callback.MULTIOBJ时才有效。一般来说会先通过查询迭代次数来停止多目标优化的步骤,然后开始下一个层次目标的求解,如代码所示。
importgurobipyasgrb
model=grb.Model()
#停止多目标优化过程
importtime
defmycallback(model,where):
ifwhere==grb.GRB.Callback.MULTIOBJ:
#获取当前目标函数值
model._objcnt=model.cbGet(grb.GRB.Callback.MULTIOBJ_OBJCNT)
#重置开始计时时间
model._starttime=time.time()
#判断是否退出搜索
eliftime.time()-model._starttime>BIGorsolutionisgood_enough:
#停止搜索
model.cbStopOneMultiObj(model._objcnt)
model._objcnt=0
model._starttime=time.time()
model.optimize(mycallback)
至此,已经讲解了很多Gurobi中回调函数的用法,当然要用好回调函数不仅需要熟悉结构,还要知道Gurobi是如何求解优化模型的,更多细节还需要结合实际模型使用。
上一条:Gurobi-第六章Gurobi多目标优化
下一条:Postman中参数填写方式