You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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):
---&gt; 50         model.constraints.add((model.x[i,j]+model.x[j,i]) <= 1)
     54 results = opt.solve(model)
     56 model.x.get_values()
错误原因分析
  1. 未定义变量I:代码中使用for i in I循环,但从未定义过集合I,这会直接触发NameError(实际报错显示KeyError是因为后续索引问题掩盖了这个错误,若先定义I会暴露索引问题)。
  2. 变量索引不匹配: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。
  3. 循环逻辑错误:即使定义了I,range(i)也不适用于字符串类型的集合元素(如'A'/'B'),字符串无法作为range()的参数,会触发TypeError。
修复建议

根据你限制"反平行弧对"的需求,你的问题应该是有向图上的指派问题(每个节点既是指派的源也是汇,即集合M和W为同一个节点集合),而非二分图指派。修复步骤如下:

  1. 统一节点集合:将M和W合并为同一个集合,比如定义N = ['A', 'B', 'C'],同时修正成本矩阵c的索引为节点对(包括反向弧的成本)。
  2. 修正变量定义:基于同一集合定义model.x,若不需要自环可添加排除条件。
  3. 修正约束循环逻辑:遍历所有节点对(i,j)且i < j,添加反平行弧约束。
  4. 修正指派约束:确保每个节点的出度和入度都为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 06:00:16