Pyomo添加反平行弧约束报错的问题排查与修复请求
问题描述
建模带额外约束的指派问题,要求解中不能包含反平行弧对(即所有二元变量满足 x[i,j] + x[j,i] ≤ 1)。复制现有指派问题解决方案后添加约束,运行代码时出现KeyError错误,测试代码及报错信息如下:
""" Toy example for testing assignment without anti-parallel arcs """ from pyomo.environ import * from pyomo.opt import * opt = solvers.SolverFactory("glpk") # Change to "ipopt" for interior point solver """ a larger instance that could be activated: M = ['0', '1', '2','3','4','5','6'] W = ['0', '1', '2','3','4','5','6'] c = {('0','0'):0, ('0','1'):1, ('0','2'):0, ('0','3'):0, ('0','4'):0, ('0','5'):0, ('0','6'):0, ('1','0'):1, ('1','1'):0, ('1','2'):0, ('1','3'):0, ('1','4'):0, ('1','5'):0, ('1','6'):0, ('2','0'):0, ('2','1'):0, ('2','2'):0, ('2','3'):0, ('2','4'):0, ('2','5'):0, ('2','6'):0, ('3','0'):0, ('3','1'):0, ('3','2'):0, ('3','3'):0, ('3','4'):0, ('3','5'):0, ('3','6'):0, ('4','0'):0, ('4','1'):0, ('4','2'):0, ('4','3'):0, ('4','4'):0, ('4','5'):0, ('4','6'):0, ('5','0'):0, ('5','1'):0, ('5','2'):0, ('5','3'):0, ('5','4'):0, ('5','5'):0, ('5','6'):0, ('6','0'):0, ('6','1'):0, ('6','2'):0, ('6','3'):0, ('6','4'):0, ('6','5'):0, ('6','6'):0} """ M = ['A', 'B', 'C'] W = ['D', 'E', 'F'] c = {('A','D'):1, ('A','E'):3, ('A','F'):3, ('B','D'):4, ('B','E'):3, ('B','F'):2, ('C','D'):5, ('C','E'):4, ('C','F'):2} model = ConcreteModel() model.x = Var(M, W, within=Binary) model.z = Objective(expr = sum(c[i,j]*model.x[i,j] for i in M for j in W), sense=maximize) def all_m_assigned_rule (model, i): return sum(model.x[i,j] for j in W) == 1 model.m = Constraint(M, rule=all_m_assigned_rule) def all_w_assigned_rule (model, j): return sum(model.x[i,j] for i in M) == 1 model.w = Constraint(W, rule=all_w_assigned_rule) # additional model constraints that fail: model.constraints = ConstraintList() for i in I: for j in range(i): model.constraints.add((model.x[i,j]+model.x[j,i]) <= 1)
报错信息:
KeyError Traceback (most recent call last) Input In [12], in <cell line: 48>() 48 for i in I: 49 for j in range(i): ---> 50 model.constraints.add((model.x[i,j]+model.x[j,i]) <= 1) 54 results = opt.solve(model) 56 model.x.get_values()
错误原因分析
- 未定义变量
I:代码中使用for i in I循环,但从未定义过集合I,这会直接触发NameError(实际报错显示KeyError是因为后续索引问题掩盖了这个错误,若先定义I会暴露索引问题)。 - 变量索引不匹配:
model.x是基于两个不同集合M(工人:A/B/C)和W(任务:D/E/F)定义的二元变量,仅存在x[i,j](i∈M, j∈W),不存在x[j,i](j∈W, i∈M)——因为j不在M集合中、i不在W集合中,访问该索引会触发KeyError。 - 循环逻辑错误:即使定义了
I,range(i)也不适用于字符串类型的集合元素(如'A'/'B'),字符串无法作为range()的参数,会触发TypeError。
修复建议
根据你限制"反平行弧对"的需求,你的问题应该是有向图上的指派问题(每个节点既是指派的源也是汇,即集合M和W为同一个节点集合),而非二分图指派。修复步骤如下:
- 统一节点集合:将
M和W合并为同一个集合,比如定义N = ['A', 'B', 'C'],同时修正成本矩阵c的索引为节点对(包括反向弧的成本)。 - 修正变量定义:基于同一集合定义
model.x,若不需要自环可添加排除条件。 - 修正约束循环逻辑:遍历所有节点对
(i,j)且i < j,添加反平行弧约束。 - 修正指派约束:确保每个节点的出度和入度都为1(符合有向指派问题的要求)。
修复后的完整代码示例:
""" Toy example for testing assignment without anti-parallel arcs """ from pyomo.environ import * from pyomo.opt import * opt = solvers.SolverFactory("glpk") # 统一节点集合 N = ['A', 'B', 'C'] # 定义包含反向弧的成本矩阵,自环成本设为极大值避免被选择 c = {('A','B'):3, ('A','C'):3, ('B','A'):4, ('B','C'):2, ('C','A'):5, ('C','B'):4, ('A','A'):100, ('B','B'):100, ('C','C'):100} model = ConcreteModel() # 定义节点间的二元变量,排除自环 model.x = Var(N, N, within=Binary, exclude=(lambda i,j: i==j)) # 最大化总收益 model.z = Objective(expr = sum(c[i,j]*model.x[i,j] for i in N for j in N if i != j), sense=maximize) # 每个节点出度为1 def out_degree_rule(model, i): return sum(model.x[i,j] for j in N if i != j) == 1 model.out_degree = Constraint(N, rule=out_degree_rule) # 每个节点入度为1 def in_degree_rule(model, j): return sum(model.x[i,j] for i in N if i != j) == 1 model.in_degree = Constraint(N, rule=in_degree_rule) # 添加反平行弧约束:x[i,j] + x[j,i] ≤1 model.anti_parallel = ConstraintList() for idx_i, i in enumerate(N): for idx_j in range(idx_i): j = N[idx_j] model.anti_parallel.add(model.x[i,j] + model.x[j,i] <= 1) # 求解并输出结果 results = opt.solve(model) model.x.display()
内容的提问来源于stack exchange,提问作者Manfred Weis
相关产品推荐
相关产品推荐

