gurobi中callback函数的使用整理

在初始使用的时候,很多人觉得,最起码我是这么觉得,gurobi是个黑箱。即:只要输入决策变量,目标函数,和约束,直接调用model.optimize()即可,至于optimize是怎么求解,不了解不知道也没办法干预。

但其实不是的。

model.optimize()的完整形式是model.optimize(callback=none),既我们没有赋予括号中任何信息。但其实callback功能十分强大,在后期实现分支定价,分支切割算法的时候经常用。

callback可以实现监视,干预,也就是部分程度和gurobi求解器实现交流交互,管理gurobi的优化进程。更多callback的分析可以看gurobi自带的参考资料,即refman.pdf文件。

1.callback的使用

首先需要定义callback即回调函数,然后model.optimize(所定义的callback函数),即传入定义的callback函数即可。

而定义callback函数,有固定的模式,是半定制的形式(不像我们编程其它的函数,从头到尾都是自己定义;也不是全部定义好了,只需调用)。

callback里头的参数是where和what两个,where的取值直接决定了what的取值,即在什么地方(where)获取什么信息(what),因此二者要正确对应。where是callback函数的触发点,表明了我们在哪里进行callback的调用;而what是要进行的操作,即能够获取什么信息。其中where的取值有9种,what的取值也有很多种。比较常用的what取值是:

        MIP_SOL:当前解的具体取值

        MIP_OBJ:新解的目标值

        MIP_OBJBST:当前最优的目标值

        MIP_OBJBND:当前最优界

        MIP_ODCNT:当前已搜索的节点数

        MIP_SOLCNT:当前发现可行解的数量

        MIP_CUTCNT:当前割平面使用次数

        MIP_NODLFT:当前未搜索的节点数

        MIP_ITRCNT:当前单纯形迭代步数

        ...

调用也很简单,如下述:

if where== grb.GRB.Callback.MULTIOBJ:  # where
    print(model.cbGet(grb.GRB.Callback.MULTIOBJ_OBJCNT))  # what

可以看到,需要用model.cbGet(what)获取信息。

2.callback其它常用函数

除了model.cbGet(what)之外,还有其它的一些常用函数来获取运行过程中的信息或修改模型运行状态。如:

        (1)model.cbGetNodeRel(vars):在callback函数中获取当前节点的线性松弛的解中决策变量的取值,即当前节点的松弛解。vars 为要查询的变量,需要注意的是,只有在where == GRB.Callback.MIPNODE并且 CRB.Callback.MIPNODE STATUS == GRB.OPTIMAL两个条件同时成立时才能使用。

# 查询变量在当前节点的松弛解
def mycallback(model, where):
    if where == GRB.Callback.MIPNODE and model.cbGet(GRB.Callback.MIPNODE_STATUS) ==                
             GRBOPTIMAL:
        print(model.cbGetNodeRel(model._vars))

model._vars= model.getVars()
model.optimize(mycallback)

        (2)model.cbGetSolution(vars): 获取MIP的当前解(整数解),或者说查询变量在新可行解中的值。

        (3)model.cbLazy(lhs, sense, rhs):向MIP模型中添加一个lazy惰性约束,需要设置model.Params.lazyConstrains=1,即启用lazy约束。使用场景是:当模型中的约束集太大无法全部添加时(比如TSP破除子回路的约束,初始肯定不可能把所有的节点的真子集子回路避免约束全部添加进去),通常会使用lazy约束。通过只包含在分支割平面搜索过程中不满足条件的约束(即只有在被违反时才会起作用),有时也可以在只添加完整约束集的一部分时找到经验证的最优解。在添加lazy cut时应该先查询当前节点的解(通过cbGstSolution获取GRB.CB_MIPSOL或通过该cbGetNodeNodelRel获取GRB.CB_MIP_NODE)。

for m in model.getVars():
    if (m.varName.startswith('x')):
       a = (int)(m.varName.split('_')[1])  #m.varName为'x_10_9'.split('_')为['x','10','9']
       b = (int)(m.varName.split('_')[2])
       x_value[a][b] = model.cbGetSolution(m)  # 获取model中变量的值

        (4)model.cbCut(lhs, sense, rhs):用于求解MIP问题时,在节点添加割平面。虽然割平面可以添加到树枝和切割树的任何节点,但是它们会增加在每个节点处求解松弛模型的大小,并且会显著降低节点处理的速度,因此应谨慎添加。除此以外,割平面通常用于切断当前松弛解,要在当前节点上检索松弛方案,因此应该先调用cbGetNodeRel。也必须将参数precrush设置为1,即model.Params.PreCrush = 1,意思是关闭gurobi预处理对模型约束的转化。

        (5)model.cbSetsolution(vars, solution):将一个已知解(完整的),或已知解的部分信息(部分解)传递给当前模型。如将启发式求解得到的解作为上界传给gurobi,gurobi可以在它的基础上求解,从而加快模型整体求解。如果要指定多组变量的值,可以多次调用该方法。

        (6)model.cbUseSolution():使用cbUseSolution导入一个解后,可以调用cbUseSolution来计算这个解对应的目标函数。

你可能感兴趣的:(运筹优化理论,学习思考总结,备忘边角料,运筹)