使用Gurobi编写Python优化模型时的报错排查求助
回收优化模型Gurobi代码错误排查与修正
错误原因分析及修正步骤
1. KeyError: (0, 0)(原目标函数)
- 原因:变量
Z的定义与使用不匹配。根据注释,Z是标记客户是否处理某类材料的二元变量,但代码中定义为Z = model.addVars(M, T, vtype=GRB.BINARY, name="Z")(维度为材料×天数),但目标函数中却用Z[c, 0](客户索引作为第一个维度),导致索引不存在。 - 修正:重新定义
Z为客户×材料的维度,修改为:
同时目标函数对应调整为:Z = model.addVars(C, M, vtype=GRB.BINARY, name="Z")(q_p * Z[c, 'plastic'] + q_v * Z[c, 'glass']) * B[c,t]
2. TypeError: unsupported operand type(s) for -: 'gurobipy.LinExpr' and 'tupledict'
- 原因:变量命名冲突。代码中先定义了卡车容量常量
V = 130,之后又定义了工厂丢弃材料的变量V = model.addVars(M, T, ...),导致约束中的<= V实际指向变量而非常量,引发类型错误。 - 修正:重命名工厂丢弃材料的变量为
V_factory,并更新所有引用处:
约束(1)改为:V_factory = model.addVars(M, T, lb=0, vtype=GRB.CONTINUOUS, name="V_factory")model.addConstrs(gp.quicksum(R[c, t] for c in range(C)) <= 130 for t in range(T))
3. KeyError: 'Missing constraint index'
- 原因:使用
model.addConstrs时未提供有效约束索引。第二个约束的工厂存储总和限制是单一约束,不需要用addConstrs(用于批量生成约束),导致Gurobi无法生成索引。 - 修正:改用
model.addConstr定义单一约束:model.addConstr(gp.quicksum(J[m, t] for m in M for t in range(T)) <= K, name="FactoryWarehouseCapacity")
其他逻辑问题修正
除上述错误外,代码还有多处逻辑不一致:
- 客户存储约束原逻辑是总和不超过容量,通常应改为每日存储量不超过容量:
G[c,t] <= A_c[c] - 工厂存储的约束(3)和起始点(4)重复定义
J[m,0],需删除其中一个 - 约束(4)中
range(1, t-1)索引错误,应改为range(t)来累加之前所有天数的存储量
修正后的完整代码
import gurobipy as gp from gurobipy import GRB # 问题数据 T = 6 # 天数 C = 5 # 客户数 M = ['plastic', 'glass'] # 材料类型 C_p = [0, 1, 2] # 处理塑料的客户(Python索引从0开始) C_v = [3, 4] # 处理玻璃的客户 # 每个客户每天收到的材料量(行=客户,列=天数) P_ct = [[35, 28, 25, 30, 53, 24], [27, 33, 29, 24, 45, 21], [39, 32, 31, 27, 38, 22], [40, 35, 48, 30, 18, 32], [48, 22, 44, 37, 21, 31]] q_p = 2 # 塑料丢弃成本 q_v = 3 # 玻璃丢弃成本 Q = 50 # 工厂切换配置的固定成本 A_c = [50, 45, 50, 65, 65] # 客户仓库容量 V_truck = 130 # 卡车总容量 K = 250 # 工厂仓库容量 U_m = {'plastic': 110, 'glass': 90} # 每日处理上限 alpha = 1/2 # 系数 # 创建模型 model = gp.Model("RecyclingProblem") # 变量定义 # 从客户c在t天运走的材料量 R = model.addVars(C, T, lb=0, vtype=GRB.CONTINUOUS, name="R") # 客户c在t天丢弃的材料量 B = model.addVars(C, T, lb=0, vtype=GRB.CONTINUOUS, name="B") # 客户c在t天存储的材料量 G = model.addVars(C, T, lb=0, vtype=GRB.CONTINUOUS, name="G") # 工厂在t天处理材料m的量 H = model.addVars(M, T, lb=0, vtype=GRB.CONTINUOUS, name="H") # 工厂在t天丢弃材料m的量 V_factory = model.addVars(M, T, lb=0, vtype=GRB.CONTINUOUS, name="V_factory") # 工厂在t天存储材料m的量 J = model.addVars(M, T, lb=0, vtype=GRB.CONTINUOUS, name="J") # 工厂t天是否处理材料m的二元变量 X = model.addVars(M, T, vtype=GRB.BINARY, name="X") # 工厂t天是否切换配置的二元变量 Y = model.addVars(T, vtype=GRB.BINARY, name="Y") # 客户c是否处理材料m的二元变量 Z = model.addVars(C, M, vtype=GRB.BINARY, name="Z") # 目标函数:最小化总成本 objective = sum( sum((q_p * Z[c, 'plastic'] + q_v * Z[c, 'glass']) * B[c,t] for c in range(C)) + alpha * sum((q_p if m == 'plastic' else q_v) * V_factory[m,t] for m in M) + Q * Y[t] for t in range(T) ) model.setObjective(objective, GRB.MINIMIZE) # 约束条件 # (1) 每日运走材料总量不超过卡车容量 model.addConstrs( gp.quicksum(R[c, t] for c in range(C)) <= V_truck for t in range(T) ) # (2) 仓库容量限制 # 客户仓库:每日存储量不超过容量 model.addConstrs( G[c, t] <= A_c[c] for c in range(C) for t in range(T) ) # 工厂仓库:总存储量不超过容量 model.addConstr( gp.quicksum(J[m, t] for m in M for t in range(T)) <= K, name="FactoryWarehouseTotalCapacity" ) # (3) 客户存储平衡:当日存储 = 前日存储 + 当日到货 - 当日运走 - 当日丢弃 model.addConstrs( G[c, 0] == P_ct[c][0] - R[c, 0] - B[c, 0] for c in range(C) ) for c in range(C): for t in range(1, T): model.addConstr( G[c, t] == G[c, t-1] + P_ct[c][t] - R[c, t] - B[c, t], name=f"ClientStorageBalance_{c}_{t}" ) # (4) 工厂存储平衡:当日存储 = 前日存储 + 当日收到材料 - 当日处理 - 当日丢弃 model.addConstrs( J[m, 0] == gp.quicksum(R[c, 0] for c in (C_p if m == 'plastic' else C_v)) - H[m, 0] - V_factory[m, 0] for m in M ) for m in M: for t in range(1, T): model.addConstr( J[m, t] == J[m, t-1] + gp.quicksum(R[c, t] for c in (C_p if m == 'plastic' else C_v)) - H[m, t] - V_factory[m, t], name=f"FactoryStorageBalance_{m}_{t}" ) # (5) 处理量上限:只有开启处理时才能生产 model.addConstrs( H[m,t] <= U_m[m] * X[m,t] for m in M for t in range(T) ) # (6) 每日最多处理一种材料 model.addConstrs( gp.quicksum(X[m, t] for m in M) <= 1 for t in range(T) ) # 求解模型 model.optimize() # 输出结果 if model.status == GRB.OPTIMAL: print("找到最优解:") print(f"目标函数值: {model.objVal:.2f}") print("变量值(仅非零值):") for var in model.getVars(): if abs(var.x) > 1e-6: print(f"{var.varName}: {var.x:.2f}") else: print("无法找到最优解。")
补充说明
- 修正了客户索引问题(Python从0开始,原代码中C_p/C_v的1-5改为0-4)
- 补充了工厂材料区分逻辑(塑料客户的运量仅进入塑料存储,玻璃同理)
- 完善了存储平衡逻辑,加入前日存储累积(原代码未考虑,导致逻辑缺失)
- 添加了处理开关变量X和处理量H的关联约束,确保逻辑自洽
内容的提问来源于stack exchange,提问作者aaaaaaaa2
相关产品推荐
相关产品推荐

