【问题标题】:Pyomo scheduling optimization problem with non-continuous objective function具有非连续目标函数的 Pyomo 调度优化问题
【发布时间】: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


    【解决方案1】:

    这可以线性建模,无需二元变量。

    使用类似的东西:

     totalHours = normalHours + overtimeHours
     normalHours <= 40
     cost = normalWage*normalHours + overtimePay*overtimeHours
    

    我们很幸运:加班费比较贵,所以我们会自动先用完正常时间。

    【讨论】:

      猜你喜欢
      • 2023-02-10
      • 2022-06-16
      • 1970-01-01
      • 2018-11-17
      • 1970-01-01
      • 2017-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多