【问题标题】:Gurobi prefix sum optimizationGurobi prefix sum optimization
【发布时间】:2019-09-14 09:16:09
【问题描述】:

考虑以下 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&lt;N 的总和。前一种情况是线性时间,而后一种情况是二次的。我有一个包含许多这样的总和和约束的 LP,我想知道是否最好创建一个单独的变量来存储每个序列的前缀总和,以防止 GurobiPy 重新计算每个约束的总和,但我不如果它已经足够聪明,就不想这样做。

【问题讨论】:

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


    【解决方案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 秒。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        猜你喜欢
        • 2018-04-07
        • 1970-01-01
        • 1970-01-01
        • 2012-05-06
        • 1970-01-01
        • 2022-12-27
        • 2016-08-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多