【发布时间】:2021-05-20 02:37:39
【问题描述】:
我有以下优化问题:
将一组用户分配到一组班次,以最大限度地降低劳动力成本。每个用户都有自己的小时工资,但需要注意的是,任何超过特定加班阈值的小时数都需要计入工资乘数。例如。如果阈值为 5 小时且轮班时间为 8 小时,则 5 小时将支付普通用户工资,其余 3 小时将支付工资乘以预定义因子。并且它会延续到同一日期范围内(例如一周)稍后工作的班次,因此如果周一班次可能会导致周五班次计入加班。
Example:
threshold: 5
multiplier: 2
wage: 10,
shift duration: 8
cost = 5 * 10 + (8 - 5) * 10 * 2 = 110
我正在使用 pyomo 库为 python 建模我的问题,我遇到了一个问题
Evaluating Pyomo variables in a Boolean context, e.g.
这是我尝试运行的完整示例代码:
import numpy
from datetime import datetime
from pyomo.environ import *
from pyomo.opt import SolverFactory
users = ['U1', 'U2', 'U3']
shifts = ['S1', 'S2'] # shifts in a chronological order
user_data = {
'U1': dict(wage=10),
'U2': dict(wage=20),
'U3': dict(wage=30),
}
shifts_data = {
'S1': dict(dtstart=datetime(2020, 2, 15, 9, 0), dtend=datetime(2020, 2, 15, 18, 0)),
'S2': dict(dtstart=datetime(2020, 2, 15, 19, 0), dtend=datetime(2020, 2, 15, 23, 0))
}
OVERTIME_THRESHOLD = 5 # hours
OVERTIME_MULTIPLIER = 2
model = ConcreteModel()
# (user, shift) binary pairs. If 1 then "user" works the given "shift"
model.assignments = Var(((user, shift) for user in users for shift in shifts), within=Binary, initialize=0)
def get_shift_hours(shift):
return (shifts_data[shift]['dtend'] - shifts_data[shift]['dtstart']).total_seconds() / 3600
def get_shift_cost(m, shift, shift_index, user):
shift_hours = get_shift_hours(shift)
all_hours_including_shift = sum(get_shift_hours(s) * m.assignments[user, s] for i, s in enumerate(shifts) if i <= shift_index)
# overtime hours are any hours above the OVERTIME_THRESHOLD threshold
ot_hours_including_shift = max(0, all_hours_including_shift - OVERTIME_THRESHOLD)
all_hours_excluding_shift = sum(get_shift_hours(s) * m.assignments[user, s] for i, s in enumerate(shifts) if i < shift_index)
ot_hours_excluding_shift = max(0, all_hours_excluding_shift - OVERTIME_THRESHOLD)
shift_ot_hours = ot_hours_including_shift - ot_hours_excluding_shift
shift_reg_hous = shift_hours - shift_ot_hours
return user_data[user]['wage'] * (shift_reg_hous + OVERTIME_MULTIPLIER * shift_ot_hours)
def obj_rule(m):
s = 0
# if a shift gets scheduled it has a negative impace on the objective function so it maximizes the number of scheduled shifts
s = s - sum(m.assignments[user, shift] * 1000000 for user in users for shift in shifts)
for user in users:
for shift_index, shift in enumerate(shifts):
# add the cost of a shift if the "user" was assigned to it (via the binary decision variable)
s = s + m.assignments[user, shift] * get_shift_cost(m, shift, shift_index, user)
return s
model.constraints = ConstraintList()
"""
Constraints that ensure the same user is not scheduled for overlapping shifts
"""
def shifts_overlap(shift_1, shift_2):
s1 = shifts_data[shift_1]
s2 = shifts_data[shift_2]
return s2['dtstart'] < s1['dtend'] and s2['dtend'] > s1['dtstart']
for shift_1 in shifts:
for shift_2 in shifts:
if shift_1 == shift_2 or not shifts_overlap(shift_1, shift_2):
continue
for user in users:
model.constraints.add(
1 >= model.assignments[user, shift_1] + model.assignments[user, shift_2]
)
"""
Constraints that a shift has only 1 assignee
"""
for shift in shifts:
model.constraints.add(
1 >= sum(model.assignments[user, shift] for user in users)
)
"""
End constraints
"""
model.obj = Objective(rule=obj_rule, sense=minimize)
opt = SolverFactory('cbc') # choose a solver
results = opt.solve(model) # solve the model with the selected solver
model.pprint()
我一直在阅读关于析取和分段约束的文章,但我找不到将这些概念应用于我的问题的方法。任何帮助将不胜感激,谢谢!
【问题讨论】:
标签: python optimization pyomo