简体   繁体   English

Gurobi 问题 - 添加带有回调函数的用户剪切

[英]Issue with Gurobi - Adding usercuts with callback function

I am currently working on a MILP formulation that I want to solve using Gurobi with a branch-and-cut approach.我目前正在研究一个 MILP 公式,我想使用 Gurobi 和分支切割方法来解决这个问题。 My model is a variation of a classic Pickup and Delivery Problem with Time Windows (PDPTW), for which several classes of valid inequalities are defined.我的模型是具有时间窗的经典取件和交付问题 (PDPTW) 的变体,为此定义了几类有效的不等式。 As the branch-and-bound solver runs, I want to add those inequalities (ie, I want to add cuts), if certain conditions in the current node are met.当分支定界求解器运行时,如果满足当前节点中的某些条件,我想添加那些不等式(即,我想添加切割)。 My issue is as follows:我的问题如下:

My variables are defined as dictionaries, which makes it easy to use them when formulating constraints because I can easily use their original indexing.我的变量被定义为字典,这使得在制定约束时很容易使用它们,因为我可以很容易地使用它们的原始索引。 An example of how I define variables is provided below下面提供了我如何定义变量的示例

tauOD = {}
# Start- End-Service time of trucks
for i in range(0,Nt):
    tauOD[i,0]=model.addVar(lb=0.0, ub=truckODTime[i][0],
                             vtype=GRB.CONTINUOUS,name='tauOD[%s,%s]'%(i,0))
    tauOD[i,1]=model.addVar(lb=0.0, ub=truckODTime[i][1],
                             vtype=GRB.CONTINUOUS,name='tauOD[%s,%s]'%(i,1))

Once my model is defined in terms of variables, constraints, and cost function, in a classic branch-and-bound problem I would simply use model.optimize() to start the process.一旦根据变量、约束和成本函数定义了我的模型,在经典的分支定界问题中,我将简单地使用 model.optimize() 来启动该过程。 In this case, I am using the command model.optimize(my_callback), where my_callback is the callback function I defined to add cuts.在本例中,我使用命令 model.optimize(my_callback),其中 my_callback 是我定义的用于添加剪切的回调函数。 My issue is that the callback function, for some reasons, does not like model variables defined as dictionaries.我的问题是回调函数由于某些原因不喜欢定义为字典的模型变量。 The only workaround I found is as follows:我发现的唯一解决方法如下:

model._vars = model.getVars() #---> added this call right before the optimization starts
model.optimize(mycallback)

and then inside the callback I can now retrieve variables using their ordering, not their indices as follows:然后在回调中,我现在可以使用它们的顺序检索变量,而不是它们的索引,如下所示:

def mycallback(model,where):
    if where == GRB.Callback.MIPNODE:
        status = model.cbGet(GRB.Callback.MIPNODE_STATUS)
        # If current node was solved to optimality, add cuts to strenghten
        # linear relaxation
        if status == GRB.OPTIMAL:
            this_Sol =  model.cbGetNodeRel(model._vars) # Get variables of current solution
            # Adding a cut
            model.cbCut(lhs=this_Sol[123]+this_Sol[125],sense=GRB.LESS_EQUAL,rhs=1) #---> Dummy cut just 
                                                                                    # for illustration 
                                                                                    # purposes

The aforementioned cut is just a dummy example to show that I can add cuts using the order variables are sequenced in my solution, and not their indexing.上述切割只是一个虚拟示例,表明我可以使用在我的解决方案中排序的顺序变量来添加切割,而不是它们的索引。 As example, I would like to be able to write a constraint inside my callback as例如,我希望能够在我的回调中编写一个约束作为

x[0,3,0] + x[0,5,0] <= 1 x[0,3,0] + x[0,5,0] <= 1

but the only thing I can do is to write但我唯一能做的就是写

this_Sol[123] + this_Sol[125] <= 1 (assuming x[0,3,0] is the 124-th variable of my solution vector, and x[0,5,0] is the 126-th). this_Sol[123] + this_Sol[125] <= 1(假设 x[0,3,0] 是我的解向量的第 124 个变量,而 x[0,5,0] 是第 126 个)。 Although knowing the order of variables is doable, because it depends on how I create them when setting up the model, it is a much more challenging process (and error-prone) rather than being able to use the indices, as I do when defining the original constraints of my model (see below for an example):虽然知道变量的顺序是可行的,因为这取决于我在设置模型时如何创建它们,这是一个更具挑战性的过程(并且容易出错),而不是像我在定义时那样使用索引我的模型的原始约束(请参见下面的示例):

###################
### CONSTRAINTS ###
###################
# For each truck, one active connection from origin depot
for i in range(0,Nt):
    thisLHS = LinExpr()
    for j in range(0,sigma):
        thisLHS += x[0,j+1,i]
    thisLHS += x[0,2*sigma+1,i]
    model.addConstr(lhs=thisLHS, sense=GRB.EQUAL, rhs=1,
                            name='C1_'+str(i))

Did any of you experience a similar problem?你们中有人遇到过类似的问题吗? A friend of mine told me that Gurobi, for some reasons, does not like variables defined as dictionaries inside a callback function, but I do not know how to circumvent this.我的一个朋友告诉我,由于某些原因,Gurobi 不喜欢在回调函数中定义为字典的变量,但我不知道如何规避这一点。 Any help would be greatly appreciated.任何帮助将不胜感激。 Thanks!谢谢!

Alessandro亚历山德罗

You should make a copy of the variables by their dicts.您应该通过它们的字典复制变量。

To get the variable index, you also have to make a copy of the lists os indexes.要获取变量索引,您还必须复制列表 os 索引。

Try this:尝试这个:

model._I = model.I
model._J = model.J
model._K = model.K
model._x = model.x

You need theses indexes lists so you can loop each target variable x to verify some condition.您需要这些索引列表,以便您可以循环每个目标变量 x 以验证某些条件。 As you would do writing a normal constraint for your model.就像您为模型编写法线约束一样。

Then inside your callback you can make the index iterations:然后在您的回调中,您可以进行索引迭代:

def mycallback(model,where):
    if where == GRB.Callback.MIPNODE:

        x = model.cbGetSolution(model._x)

        for i in model._I:
            if sum([x[i,j,k] for j in model._J for k in model._K]) > 1:
                Add_the_cut()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM