建立一个目标功能,涉及在PYOMO中计数以解决分配问题
我的PYOMO模型正在尝试解决一个任务分配问题,其中需要将4个工人分配给8个任务,因此每个工作人员是2个任务。
目标函数之一model.obj2
试图最大程度地减少每个工人使用的材料类型的总和。原因是因为每辆将材料运送到工人的卡车只能携带1种材料,因此有效率提高以最大程度地减少卡车访问的总数。
目前正在使用len(set(...))
查找分配给工人的两个任务使用的唯一材料的数量,sum()
添加为所有4名工人提高此号码。
def obj_rule(m):
# Minimize the total costs
obj1 = sum(
costs[i][j] * model.x[w, t] for i, w in enumerate(W) for j, t in enumerate(T)
)
# Minimize the number of unique materials used per worker
obj2 = len(
set(
material
for w in W
for t in T
for material in materials_used[t]
if value(model.x[w, t]) == True
)
)
return 5 * obj1 + 2 * obj2
但是,删除model.obj1
(用于调试目的),例如
def obj_rule(m):
# Minimize the number of unique materials used per worker
obj2 = len(
set(
material
for w in W
for t in T
for material in materials_used[t]
if value(model.x[w, t]) == True
)
)
return obj2
警告中的结果
警告:检测到不断的目标,用占位符代替以防止 求解器故障。
这可能解释了为什么model.obj2
在初始代码中似乎并未最小化。客观表达可能已转换为标量值?
我可以得到一些帮助以改写PYOMO的正确方法吗?谢谢你!
复制问题的代码
from pyomo.environ import *
import numpy as np
# 4 workers X 8 tasks
costs = np.array(
[
# workerA
[1, 2, 3, 4, 5, 6, 7, 8],
[1, 2, 3, 4, 5, 6, 7, 8],
# workerB
[8, 7, 6, 5, 4, 3, 2, 1],
[8, 7, 6, 5, 4, 3, 2, 1],
# workerC
[1, 3, 5, 7, 9, 11, 13, 15],
[1, 3, 5, 7, 9, 11, 13, 15],
# workerD
[15, 13, 11, 9, 7, 5, 3, 1],
[15, 13, 11, 9, 7, 5, 3, 1],
]
)
# "stone", "wood", "marble", "steel", "concrete"
materials_used = {
"taskA": ["stone", "wood"],
"taskB": ["marble", "wood"],
"taskC": ["marble", "stone"],
"taskD": ["steel", "stone"],
"taskE": ["marble", "steel"],
"taskF": ["marble", "steel"],
"taskG": ["concrete", "marble"],
"taskH": ["concrete", "steel"],
}
W = [
"workerA1",
"workerA2",
"workerB1",
"workerB2",
"workerC1",
"workerC2",
"workerD1",
"workerD2",
]
T = ["taskA", "taskB", "taskC", "taskD", "taskE", "taskF", "taskG", "taskH"]
model = ConcreteModel()
model.x = Var(W, T, within=Binary, initialize=0)
def obj_rule(m):
# Minimize the total costs
# obj1 = sum(
# costs[i][j] * model.x[w, t] for i, w in enumerate(W) for j, t in enumerate(T)
# )
# Minimize the number of unique materials used per worker
obj2 = len(
set(
material
for w in W
for t in T
for material in materials_used[t]
if value(model.x[w, t]) == True
)
)
return obj2
# return 5 * obj1 + 2 * obj2
model.obj = Objective(
rule=obj_rule,
sense=minimize,
)
def all_t_assigned_rule(m, w):
return sum(m.x[w, t] for t in T) == 1
def all_w_assigned_rule(m, t):
return sum(m.x[w, t] for w in W) == 1
model.c1 = Constraint(W, rule=all_t_assigned_rule)
model.c2 = Constraint(T, rule=all_w_assigned_rule)
opt = SolverFactory("glpk")
results = opt.solve(model)
My Pyomo model is trying to solve a task assignment problem where 4 workers needs to be assigned to 8 tasks, so that's 2 tasks per worker.
One of the objective function model.obj2
tries to minimize the sum of the types of materials used by each worker worker. The reason is because every truck transporting materials to the worker can only carry 1 type of material, so there is efficiency gains to minimize the total number of truck visits.
This is currently being done using len(set(...))
to find number of unique materials used by both tasks assigned to a worker, and sum()
to add up this number for all 4 workers.
def obj_rule(m):
# Minimize the total costs
obj1 = sum(
costs[i][j] * model.x[w, t] for i, w in enumerate(W) for j, t in enumerate(T)
)
# Minimize the number of unique materials used per worker
obj2 = len(
set(
material
for w in W
for t in T
for material in materials_used[t]
if value(model.x[w, t]) == True
)
)
return 5 * obj1 + 2 * obj2
However, removing model.obj1
(for debugging purposes), such as
def obj_rule(m):
# Minimize the number of unique materials used per worker
obj2 = len(
set(
material
for w in W
for t in T
for material in materials_used[t]
if value(model.x[w, t]) == True
)
)
return obj2
results in the warning
WARNING: Constant objective detected, replacing with a placeholder to prevent
solver failure.
This might explain why model.obj2
does not seem to be minimized for in the initial code. The objective expression might have been converted into a scalar value?
Can I get some help to rewrite this objective function the proper way for Pyomo? Thank you!
Code to reproduce problem
from pyomo.environ import *
import numpy as np
# 4 workers X 8 tasks
costs = np.array(
[
# workerA
[1, 2, 3, 4, 5, 6, 7, 8],
[1, 2, 3, 4, 5, 6, 7, 8],
# workerB
[8, 7, 6, 5, 4, 3, 2, 1],
[8, 7, 6, 5, 4, 3, 2, 1],
# workerC
[1, 3, 5, 7, 9, 11, 13, 15],
[1, 3, 5, 7, 9, 11, 13, 15],
# workerD
[15, 13, 11, 9, 7, 5, 3, 1],
[15, 13, 11, 9, 7, 5, 3, 1],
]
)
# "stone", "wood", "marble", "steel", "concrete"
materials_used = {
"taskA": ["stone", "wood"],
"taskB": ["marble", "wood"],
"taskC": ["marble", "stone"],
"taskD": ["steel", "stone"],
"taskE": ["marble", "steel"],
"taskF": ["marble", "steel"],
"taskG": ["concrete", "marble"],
"taskH": ["concrete", "steel"],
}
W = [
"workerA1",
"workerA2",
"workerB1",
"workerB2",
"workerC1",
"workerC2",
"workerD1",
"workerD2",
]
T = ["taskA", "taskB", "taskC", "taskD", "taskE", "taskF", "taskG", "taskH"]
model = ConcreteModel()
model.x = Var(W, T, within=Binary, initialize=0)
def obj_rule(m):
# Minimize the total costs
# obj1 = sum(
# costs[i][j] * model.x[w, t] for i, w in enumerate(W) for j, t in enumerate(T)
# )
# Minimize the number of unique materials used per worker
obj2 = len(
set(
material
for w in W
for t in T
for material in materials_used[t]
if value(model.x[w, t]) == True
)
)
return obj2
# return 5 * obj1 + 2 * obj2
model.obj = Objective(
rule=obj_rule,
sense=minimize,
)
def all_t_assigned_rule(m, w):
return sum(m.x[w, t] for t in T) == 1
def all_w_assigned_rule(m, t):
return sum(m.x[w, t] for w in W) == 1
model.c1 = Constraint(W, rule=all_t_assigned_rule)
model.c2 = Constraint(T, rule=all_w_assigned_rule)
opt = SolverFactory("glpk")
results = opt.solve(model)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我认为我可以在这里添加两件事,这可能是您缺少的链接...
首先,正如评论中提到的,您输入模型的内容必须是不依赖于变量当时的值的合法表达式创建,因此 len() 等无效。解决方案:对这些类型的计数事物使用二进制变量,然后将它们通过适当的索引求和。
其次,您正确地索引了第一个变量,但您需要引入第二个变量,即决定向工作人员发送一些材料
matl
。请参阅下面的示例,该示例引入了此变量,然后使用 big-M 约束将两个决策链接在一起......具体来说,确保模型向工作人员提供所需的材料以完成所需的任务。代码:
产量:
I think there are 2 things I can add here that might be your missing links...
First, as mentioned in comments, the stuff you feed into the model must be legal expressions that do not depend on the value of the variables at time of creation, so
len()
etc. are invalid. Solution: use binary variables for those types of counting things and then sum them over appropriate indices.Second, you are indexing your first variable correctly, but you have a second variable you need to introduce, namely, the decision to send worker
w
some materialmatl
. See my example below that introduces this variable and then uses a big-M constraint to link the two decisions together.... Specifically, ensure the model delivers a required material to a worker for a task in which it is required.Code:
Yields: