Gurobi gurobipy实现嵌套求和乘积约束报QuadExpr不可迭代错误
报错原因
- 类型错误直接原因:
grb.quicksum()只接受可迭代的变量/表达式序列作为输入,你的代码中外层grb.quicksum直接传入了两个quicksum相乘得到的单个QuadExpr二次对象,不是可迭代结构,因此触发报错。 - 求和逻辑错误:原公式是遍历每个a∈A,单独计算i、j在该a下b维度的x求和项,两者相乘后再对所有a求和,你写的迭代逻辑把b也放到了跨a的求和层,维度和公式完全不匹配。
- 模型阶数错误:原约束中0-1变量
y_{i,j}与两个x求和项的乘积(二次项)继续相乘,会生成三次项,Gurobi原生不支持三次及以上的非线性规划,必须通过辅助变量加大M法线性化三次乘积部分才能求解。
修复方案
- 修正求和逻辑:遍历每个a单独计算b维度的x求和,相乘后累加得到括号内的二次表达式,避免错误嵌套
quicksum。 - 引入辅助变量
aux[i,j]表示y[i,j]和括号内二次表达式的乘积,用大M法对三次乘积做线性化。大M取括号内表达式的理论上界:x是0-1变量,单个b维度求和最大值为len(set_B),两个求和相乘最大值为len(set_B)**2,对a求和后最大值为len(set_A)*len(set_B)**2,加1后得到M = len(set_A)*len(set_B)**2 + 1。 - 线性化约束规则:
- 当
y[i,j] = 0时,强制aux[i,j] = 0 - 当
y[i,j] = 1时,强制aux[i,j] = 括号内的表达式值
修复后可运行代码
import gurobipy as grb m = grb.Model() set_I = range(2) set_J = range(2) set_A = range(2) set_B = range(2) # 计算大M上界 M = len(set_A) * (len(set_B) ** 2) + 1 x_vars = {(i,a,b):m.addVar(vtype=grb.GRB.BINARY, name=f"x_{i}_{a}_{b}") for i in set_I for a in set_A for b in set_B} y_vars = {(i,j): m.addVar(vtype=grb.GRB.BINARY, name=f"y_{i}_{j}") for i in set_I for j in set_J} c_vars = {(i,j): m.addVar(vtype=grb.GRB.CONTINUOUS, name=f"c_{i}_{j}") for i in set_I for j in set_J} # 三次乘积的线性化辅助变量 aux_vars = {(i,j): m.addVar(vtype=grb.GRB.CONTINUOUS, name=f"aux_{i}_{j}") for i in set_I for j in set_J} for i in set_I: for j in set_J: # 按公式正确计算括号内的二次表达式 sum_a = 0 for a in set_A: sum_b_i = grb.quicksum(x_vars[i,a,b] for b in set_B) sum_b_j = grb.quicksum(x_vars[j,a,b] for b in set_B) sum_a += sum_b_i * sum_b_j inner_expr = sum_a + 1 # 大M法线性化乘积项 y * inner_expr = aux m.addConstr(aux_vars[i,j] >= 0) m.addConstr(aux_vars[i,j] <= M * y_vars[i,j]) m.addConstr(aux_vars[i,j] <= inner_expr) m.addConstr(aux_vars[i,j] >= inner_expr - M * (1 - y_vars[i,j])) # 原约束:如果需要严格匹配公式的等号,把sense改为grb.GRB.EQUAL即可 m.addConstr( lhs=aux_vars[i,j], sense=grb.GRB.GREATER_EQUAL, rhs=c_vars[i,j] ) # 测试求解 m.optimize()
修复后的模型最高阶数为2,属于Gurobi支持的混合整数二次约束规划(MIQCP)类型,运行不会触发类型错误,求和逻辑完全匹配给出的公式,线性化处理后可以被Gurobi正常求解。
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

