Gurobi前缀总和优化

时间:2019-04-25 02:02:16

标签: python optimization linear-programming gurobi integer-programming

请考虑以下Gurobi模型:

import gurobipy as gb
import numpy as np
N = 100
x = np.random.randint(10, high=2*N, size=N)
model = gb.Model("ACC")
amp_i_vars = model.addVars(N, vtype=gb.GRB.BINARY, name='ai')
model.setObjective(amp_i_vars.sum(*), gb.GRB.MINIMIZE)
model.addConstrs(gb.quicksum(amp_i_vars[i] for i in range(r+1)) <= x[r] 
                 for r in range(N), "SumConstr")

实际上,我们只是在尝试用尽可能多的位填充ai,以使直到位置r的位总和永远不大于x[r]

我的问题是,GurobiPy通过约束的方式是否“聪明”,即,如果它计算的前缀总和为ai,或者实际上是重新计算每个r<N的总和。前一种情况是线性时间,而后者则是二次时间。我有一个包含许多这样的总和和约束的LP,我想知道是否最好创建一个单独的变量来存储每个序列的前缀总和,以防止GurobiPy重新计算每个约束的总和,但是我不知道如果它已经足够聪明了,就不必这样做。

2 个答案:

答案 0 :(得分:1)

您的精确公式具有O(N ^ 2)个非零值,因此您需要使用O(N ^ 2)算法来构建它。您可以避免通过此更多过程循环来重新创建表达式。

import gurobipy as grb
import numpy as np
np.random.seed(10)

N = 5000
x = np.random.randint(10, high=2*N, size=N)
obj = -np.random.randint(10, high=2*N, size=N)
model = gb.Model("ACC")

# more interesting objective
amp_i_vars = model.addVars(N, vtype=grb.GRB.BINARY, name='ai', obj=obj)
model.update()
cum = grb.LinExpr()
for i, ai in amp_i_vars.items():
    cum += ai
    model.addConstr(cum <= x[i])
model.optimize()

但是,您可以通过添加表示累积和的变量的并行列表并使用递归来用O(n)非零来表示等效模型 cum [i] = cum [i-1] + x [i] 。这也将导致求解速度更快的模型。

import gurobipy as grb
import numpy as np
N = 5000
np.random.seed(10)
x = np.random.randint(10, high=2*N, size=N)
obj = -np.random.randint(10, high=2*N, size=N)
model = gb.Model("ACC")

# more interesting objective function
amp_i_vars = model.addVars(N, vtype=grb.GRB.BINARY, name='ai', obj=obj)
# since cum_vars are variables, use simple upper bound
cum_vars = model.addVars(N, vtype=grb.GRB.CONTINUOUS, name='cum', ub=x)

prev_cum = 0
for i, (ai, cum) in enumerate(zip(amp_i_vars.values(), cum_vars.values())):
    model.addConstr(cum == prev_cum + ai, name="sum_constr." + str(i))
    prev_cum = cum
model.optimize()

对于N = 5000,这可以在0.5秒内解决,而对于密集模型则需要16秒。

答案 1 :(得分:0)

在建模层上,gurobipy将“不聪明”并应用您描述的替换,因此它将一一生成约束,每次都重新计算部分和。您可以尝试为这些部分和引入辅助变量,但我的猜测是,“ 只有在总和非常大的情况下,“哑”方法才变得明显。